Skip to content

docs(incidents): list the entity types that support incidents - #18685

Open
cnpierrepapi wants to merge 9 commits into
datahub-project:masterfrom
cnpierrepapi:docs-incidents-supported-entities
Open

cnpierrepapi wants to merge 9 commits into
datahub-project:masterfrom
cnpierrepapi:docs-incidents-supported-entities

Conversation

@cnpierrepapi

Copy link
Copy Markdown
Contributor

The raiseIncident docs describe resourceUrn as "dataset, dashboard, chart,
dataFlow, etc". I read the "etc" as open-ended and tried to raise an incident on
an mlModel, which is rejected at the entity layer:

"urn:li:mlModel:(urn:li:dataPlatform:mlflow,workforce-classifier_3,PROD) is not a valid destination"

The set is not open-ended. It is whichever entities declare the
incidentsSummary aspect, and from metadata-models/src/main/resources/entity-registry.yml
on master that is exactly:

dataset, dataJob, dataFlow, chart, dashboard, service, aiAgent.

So this replaces the "etc" with the actual list, shows the error you get
otherwise, and adds a short note that ML entities are not included.

The ML note is the reason I bothered. Raising an incident on a model is a
natural thing to reach for when the thing that went wrong is a model, and the
current wording gives no reason to think it will not work. The workaround is
easy once you know: raise it on the dataset the model trained on, which is one
lineage hop away, and name the model in the title.

Two smaller things I noticed while working on this, not included here because
I am less sure they are documentation problems rather than intentional. Happy
to open separate issues if either is useful:

  • service and aiAgent support incidents but do not appear in any docs I
    could find.
  • There is no mutation to delete an incident, only to resolve one, so anything
    raised while testing stays in the instance.

Docs only, no code changes.

@github-actions github-actions Bot added docs Issues and Improvements to docs community-contribution PR or Issue raised by member(s) of DataHub Community labels Jul 28, 2026
@maggiehays maggiehays added the needs-review Label for PRs that need review from a maintainer. label Jul 28, 2026
@JonathanSolvesProblems

Copy link
Copy Markdown

+1 on documenting this. I hit the same limitation independently on DataHub Core v1.5.0.6, while trying to raise an incident on an mlModel from an agent, and arrived at exactly the workaround this PR recommends: raise it on the dataset the model trained on, and name the model in the body.

Adding the other error message for searchability, since it differs from the one in the description. A raiseIncident mutation with an mlModel URN in resourceUrns returns:

java.lang.RuntimeException: Invalid format for aspect: incident
 Cause: ERROR :: /entities/0 :: "Provided urn urn:li:mlModel:(urn:li:dataPlatform:duckdb,nyc_fare_predictor,PROD)" is invalid:
        Entity type for urn urn:li:mlModel:(urn:li:dataPlatform:duckdb,nyc_fare_predictor,PROD) is not supported

This PR quotes is not a valid destination. Both wordings are in the wild, and neither names the supported set, so a reader who searches the first string will not find the second. Worth covering both in the docs page.

One note on why this matters beyond documentation, for the ML case specifically. The model is the thing that degraded, the thing an on-call engineer opens, and the thing whose health a team watches. Because it cannot carry an incident, the model page reads healthy while the model is measurably wrong, and the signal has to live one hop upstream on a dataset that is itself fine. Supporting ML entities as incident resources, even without full health roll-up, would close that gap.

Happy to test a patch against a local v1.5.0.6 instance if that is useful.

JonathanSolvesProblems added a commit to JonathanSolvesProblems/culprit that referenced this pull request Aug 7, 2026
… criteria

Two upstream contributions filed:

- acryldata/mcp-server-datahub#200: update_description with a column_path that
  matches no field returns {"success": true} and writes nothing. Unclaimed across
  39 open issues. Includes the repro, the 14-fields-versus-0 sibling table, the
  environment, and a concrete ask.
- A comment on datahub-project/datahub#18685 with the second error message that
  DataHub Core v1.5.0.6 produces for the mlModel incident rejection, since that PR
  quotes a different string and a reader searching one will not find the other.

The datahub-ml-lineage skill remains deliberately unfiled. PR #77 ships the same
skill name and path, opened before mine. Declining it is the honest call and a
better story than a near-duplicate.

All three are now past tense with live URLs in README, SUBMISSION and
DATAHUB_FINDINGS.

Added a "For judges" table near the top of the README mapping each of the five
judging criteria, plus the open-source bonus, to the specific section and artifact
that answers it. The evidence was already in the repo but spread across a long
document, and a judge reading their thirtieth submission should not have to hunt
for it.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 1 file

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="docs/incidents/incidents.md">

<violation number="1" location="docs/incidents/incidents.md:103">
P3: The supported-type rule also applies to every entity supplied through `resourceUrns`, which the current GraphQL schema supports for multi-resource incidents. Describing only singular `resourceUrn` makes this new section incomplete for that API path; mentioning both inputs and that every destination must support `incidentsSummary` would keep the restriction accurate.</violation>

<violation number="2" location="docs/incidents/incidents.md:132">
P2: A model's training dataset is not always one lineage hop away. `mlModelTrainingData` creates a direct relationship, but the documented training-run representation is `Dataset → Training Run → ML Model` (two hops), and an instance may not have training lineage populated at all. This workaround should avoid promising a one-hop relationship and instead qualify it as a linked training dataset, directly or through a training run.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread docs/incidents/incidents.md Resolved
Comment thread docs/incidents/incidents.md Resolved
@cnpierrepapi

Copy link
Copy Markdown
Contributor Author

Confirmed, and thanks for the second string. I only ever hit the first one, so I had no way to know there were two.

They look like different checks. Yours comes back wrapped in Invalid format for aspect: incident, so that is the aspect validator refusing the write. Mine has no wrapper and reads like the destination check further out. Same result either way, nothing is written, but which message you get depends on which one you trip first. Pushed both to the docs now, normalised onto the same example URN, with a line saying they are the same failure so a search for either string lands here.

On the ML point, that is exactly the case I hit. I have an agent that does root cause on model degradation by walking lineage, and the one thing it cannot do is leave the finding on the model. It writes to the training dataset instead. So the dataset page ends up carrying a warning about something that is not wrong with the dataset, and the model page stays clean while the model is the thing that is broken. On call opens the model page.

I want to keep this PR as docs so it can land on its own. I will open a separate issue for supporting mlModel as an incident resource and tag you there. If a maintainer signals they would take it, that v1.5.0.6 test would be worth a lot, because the registry side looks small and the UI side probably is not.

@cnpierrepapi

Copy link
Copy Markdown
Contributor Author

Issue opened for the ML case: #18999. This PR stays docs only.

@JonathanSolvesProblems

Copy link
Copy Markdown

Correcting my earlier comment on this PR, because it was wrong and it should not
stand in a thread people will search.

I posted this as an mlModel rejection:

java.lang.RuntimeException: Invalid format for aspect: incident
 Cause: ERROR :: /entities/0 :: "Provided urn urn:li:mlModel:(...)" is invalid:
        Entity type for urn ... is not supported

I cannot reproduce it. On DataHub Core v1.5.0.6 a raiseIncident with an
mlModel URN in resourceUrns succeeds, and the incident persists against
the model. mlFeature too. What does reject is mlModelGroup,
mlFeatureTable and dataProcessInstance, and their message is a third wording
again: Entity type for urn: <urn> is not a valid destination for field path: /entities/*.

My best guess at what I actually hit is that I misattributed a failure whose
real cause was one of the other write-back bugs I was fixing at the time
(DATA_QUALITY is not a member of IncidentType; customType is required when
type is CUSTOM). Both fail the whole mutation regardless of the resource
URN.

Where that leaves the docs change in this PR: the supported list here comes from
the entity registry, and for mlModelGroup and mlFeatureTable it matches what
the instance does. For mlModel and mlFeature the write is accepted, so
"do not support incidents" is not quite the behaviour. The accurate statement is
that an incident on an mlModel is stored and cannot be read back, because
MLModel has no incidents field in the GraphQL schema
(Field 'incidents' in type 'MLModel' is undefined), while Dataset.incidents
exists. Might be worth a sentence, since a reader who tries it will get a
success and an incident URN.

Full sweep and raw output in #18999.

Separately, on the review note about the workaround being "one hop of lineage":
the reviewer is right to push back. In my own instance the model reaches the raw
dataset through a feature table and dbt models, not in one hop, and an instance
with no training lineage populated would not reach it at all. "A training dataset
linked to the model" is the safer phrasing.

Sorry for the noise. Better to correct it here than leave it for whoever searches
that string.

@AdrianMachado
AdrianMachado self-requested a review August 10, 2026 19:34

@AdrianMachado AdrianMachado left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment on your issue. In addition to

  1. Gaps that need to be closed and
  2. Consistency check that needs to be implemented so we have an aligned definition of "supported"

this table should be generated from code, not handwritten


How to generate the incident support table

Pattern to copy

Ingestion already generates support tables and inlines them into docs:

  1. A Python generator writes a .md.snippet under docs/generated/…
  2. The hand-authored page pulls it in with {{ inline … }}
  3. Docs build expands that at assemble time

Example today:

  • Generator: generate_operation_capture_support_table() in metadata-ingestion/scripts/docgen.py
  • Consumer: docs/api/tutorials/operations.md
{{ inline /docs/generated/ingestion/operation_capture_support_table.md.snippet }}

Do the same for incidents.

Source of truth for the table

Once the CI consistency check exists, generate from one layer only. Prefer:

entities in entity-registry.yml that declare incidentsSummary

That’s the product-facing “this asset carries incident health/summary” set. CI keeps PDL destinations and GraphQL incidents fields aligned with it.

Do not hand-copy the list into docs/incidents/incidents.md.

Minimal generator example

# metadata-ingestion/scripts/generate_incident_support_table.py
"""Generate docs/generated/incidents/supported_entities.md.snippet from the entity registry."""

from pathlib import Path

import yaml

REPO_ROOT = Path(__file__).resolve().parents[2]
REGISTRY = REPO_ROOT / "metadata-models/src/main/resources/entity-registry.yml"
OUT = REPO_ROOT / "docs/generated/incidents/supported_entities.md.snippet"

# Optional display names; fall back to raw entity type.
DISPLAY_NAMES = {
    "dataset": "Dataset",
    "dataJob": "Data Job",
    "dataFlow": "Data Flow",
    "chart": "Chart",
    "dashboard": "Dashboard",
    "service": "Service",
    "aiAgent": "AI Agent",
    "mlModel": "ML Model",
    "mlFeature": "ML Feature",
    "mlFeatureTable": "ML Feature Table",
    "schemaField": "Schema Field",
}


def entities_with_incidents_summary() -> list[str]:
    registry = yaml.safe_load(REGISTRY.read_text())
    names = []
    for entity in registry.get("entities", []):
        aspects = entity.get("aspects") or []
        if "incidentsSummary" in aspects:
            names.append(entity["name"])
    return sorted(names, key=str.casefold)


def main() -> None:
    entities = entities_with_incidents_summary()
    OUT.parent.mkdir(parents=True, exist_ok=True)

    lines = [
        "<!-- DO NOT EDIT. Generated by generate_incident_support_table.py -->",
        "",
        "| Entity | URN prefix |",
        "| --- | --- |",
    ]
    for name in entities:
        display = DISPLAY_NAMES.get(name, name)
        lines.append(f"| {display} | `urn:li:{name}:` |")

    OUT.write_text("\n".join(lines) + "\n")
    print(f"Wrote {OUT} ({len(entities)} entities)")


if __name__ == "__main__":
    main()

Example output with today’s registry:

<!-- DO NOT EDIT. Generated by generate_incident_support_table.py -->

| Entity | URN prefix |
| --- | --- |
| AI Agent | `urn:li:aiAgent:` |
| Chart | `urn:li:chart:` |
| Dashboard | `urn:li:dashboard:` |
| Data Flow | `urn:li:dataFlow:` |
| Data Job | `urn:li:dataJob:` |
| Dataset | `urn:li:dataset:` |
| Service | `urn:li:service:` |

Wire it into the docs page

In docs/incidents/incidents.md:

### Supported entity types

An entity supports incidents when it declares the `incidentsSummary` aspect.
Write validation and GraphQL read support must stay aligned with that set
(enforced by CI).

{{ inline /docs/generated/incidents/supported_entities.md.snippet }}

Hook into the docs build

Either:

  • Call the script from docgen.py / modelDocGen (same place as generate_operation_capture_support_table), or
  • Add a small Gradle dependency so :docs-website / doc generation runs it before assemble

Generated file should be gitignored or committed — follow whatever docs/generated/ingestion/*.snippet already does in this repo.


Let me know what you think

@AdrianMachado AdrianMachado self-assigned this Aug 10, 2026
@maggiehays maggiehays added pending-submitter-response Issue/request has been reviewed but requires a response from the submitter and removed needs-review Label for PRs that need review from a maintainer. labels Aug 10, 2026
@cnpierrepapi

Copy link
Copy Markdown
Contributor Author

@AdrianMachado agreed on generating it. Two things before I push, and the first one is me correcting my own PR.

The table is wrong, and generating it from incidentsSummary would keep it wrong

I wrote this against a v1.5.0.6 instance where every ML write was rejected, and I wrote "the entity must carry the incidentsSummary aspect" as though that were the write gate. It is not. The write gate is the IncidentOn relationship in IncidentInfo.pdl. Derived from master just now:

entity IncidentOn (write) incidentsSummary GraphQL incidents
dataset, chart, dashboard, dataFlow, dataJob yes yes yes
service, aiAgent yes yes no
mlModel, mlFeature, schemaField yes no no
mlModelGroup, mlFeatureTable, dataProcessInstance no no no

Ten write destinations, seven summaries, five readable.

So on master, "Anything else is rejected and nothing is written" is false, and "mlModel ... do not support incidents" is false. Those writes are accepted and the caller gets an incident URN back. They just cannot be read. That is my error, it came from generalising one version's behaviour, and it should not ship regardless of what we decide about generation.

The error strings need redoing for the same reason. Of the two I quoted, the second is the wording Jonathan could not reproduce against an mlModel, and @Ahmedxsaad hit a third wording on v1.5.0.6 that is in neither. I would rather quote what is reproducible and say which version produced it than list strings from memory.

Which is the argument for generating more than one column. A table generated from incidentsSummary alone prints exactly the seven rows I hand wrote, and still tells a reader that mlModel is unsupported while their write returns an incident URN. Generating all three columns from the three sources describes what actually happens per layer, keeps the gaps visible instead of averaging them into a yes or no, and tightens itself as #18911 lands. Same reasoning as the CI guard in #19097, applied to the page a user reads first.

On where the generator lives

You pointed at generate_operation_capture_support_table in metadata-ingestion/scripts/docgen.py. That one builds its rows from the ingestion plugin scan and never reads the entity registry, so an incidents table there means running the connector doc scan to emit seven rows.

modeldocgen.py is the closer fit. It already takes --registry ../metadata-models/src/main/resources/entity-registry.yml, already builds entity_registry: Dict[str, EntityDefinition] with .aspects per entity, and already writes into docs/generated/metamodel. It runs as :metadata-ingestion:modelDocGen, which docs-website already depends on next to docGen. The summary column is nearly free there. The PDL and GraphQL columns need two small readers, which is the same parsing #19097 does on the test side.

Happy to put it in docgen.py instead if you would rather keep docs generation in one place. Your call, it is a small difference in the wiring and none in the output.

Two details, since they came up. docs/generated/ is gitignored, so the snippet is build output and not committed, same as the ingestion one. And the file is .md.snippet while mdPrettierCheck globs **/*.md, so the generated table is not prettier's problem either way.

The failing check

markdown_format_check is only cell padding in my hand written table. prettier wants the columns aligned. It disappears with the generated snippet, and I will clear it in the meantime so this is not sitting red.

Plan

  1. Rewrite the section around the three layers and what each one controls, with the ML statement corrected and the error strings cut back to what is reproducible.
  2. Three column table generated in modeldocgen.py, inlined with {{ inline ... }}.
  3. Keep it accurate as the gaps close, rather than needing an edit per merge.

If you would rather this PR carried only the correction and the generated table came with PR1 of #18911, that is faster to review and I am happy either way. I have said in #18999 that I will take PR1, so the two would land together.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 1 file (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread docs/incidents/incidents.md Resolved
Comment thread docs/incidents/incidents.md Resolved
Comment thread docs/incidents/incidents.md Resolved
Comment thread docs/incidents/incidents.md Resolved
@maggiehays maggiehays added needs-review Label for PRs that need review from a maintainer. and removed pending-submitter-response Issue/request has been reviewed but requires a response from the submitter labels Aug 11, 2026
@cnpierrepapi
cnpierrepapi force-pushed the docs-incidents-supported-entities branch from 0dfe7dd to 50c37a4 Compare August 15, 2026 14:32
@cnpierrepapi

Copy link
Copy Markdown
Contributor Author

Pushed, four days later than it needed to be. I asked which file the generator should live in, said in the same breath that it was my call and the output was identical either way, and then waited on the answer anyway. Novel way to block yourself. Sorry for the delay.

The generator went into modeldocgen.py rather than docgen.py. It already loads the entity registry, and docs-website already depends on :metadata-ingestion:modelDocGen, so the ordering came for free and nothing new had to be wired. It is a --incident-support-output flag following the same shape as the existing --lineage-output.

Output is docs/generated/incidents/entity-support.md.snippet, gitignored like the ingestion one. The .md.snippet extension keeps it out of prettier's **/*.md glob, which also clears the markdown_format_check that was red on here.

Three columns, backend only: the IncidentOn write gate, the incidentsSummary aspect, and the extend type block in incident.graphql. I left the two frontend surfaces out on purpose. Parsing datahub-web-react from a metadata-model generator is the wrong boundary, and #19097 is a better home for keeping those from drifting. They are named in the prose with a pointer rather than dropped.

Re-derived against master before pushing: ten write destinations, seven summaries, five readable.

The correction to my own text is in. The page no longer says mlModel and mlFeature are rejected, because they are not: the write is accepted and returns an incident URN, and the problem is that nothing can read it back. The quoted rejection message now names the version it came from instead of being reproduced from memory, which is how it got wrong in the first place.

@cnpierrepapi

Copy link
Copy Markdown
Contributor Author

Two cubic threads were still open on here, and I had assumed both were stale. They were not. Both were right, and 493d62f fixes them.

The RaiseIncidentInput block on this page marked resourceUrn as String! and did not mention resourceUrns at all. On master resourceUrn is String, resourceUrns: [String!] exists, and each is required only when the other is absent. So the page told you a multi-resource incident was impossible to construct while the section immediately below it explained how the rules apply to resourceUrns. The block now matches the schema.

The second one is my error rather than an omission. I wrote that for mlModel and mlFeature "nothing reads the incident back". Incident implements Entity and the type is registered as a loadable type in GmsGraphQLEngine, so the URN the mutation returns resolves fine through the generic entity(urn: ...) query. The gap is on the asset side only: neither type can list its own incidents and neither renders them. I have corrected that in all three places it appeared, including the layer 3 description, which now says an entity without the extend type block cannot list incidents rather than that the incident cannot be fetched.

Worth noting what the shape of this was. Both findings sat under a resolved-looking pile of bot comments and both were marked outdated, because the lines they were attached to had moved. Outdated is not the same as addressed, and I treated it as if it were.

Nothing else changed. The generated table and the three-column scope are as described above, and CI was green before this push.

@maggiehays maggiehays added pending-submitter-response Issue/request has been reviewed but requires a response from the submitter and removed needs-review Label for PRs that need review from a maintainer. labels Aug 19, 2026
@AdrianMachado

Copy link
Copy Markdown
Contributor

Hold for docs rebase (2026-08-20).

Land after Danish’s mlFeatureTable PR3 so the supported-types list is true. We now track seven gates (added MCP list_incidents fragments via #18341). schemaField stays API-only (no Incidents tab) in the docs.

Tracker: #19322 / Linear OBS-2085.

@AdrianMachado

Copy link
Copy Markdown
Contributor

Unblocked (2026-08-21).

#19367 and #18911 are done. Please rebase so the supported-types docs match master (mlFeatureTable complete; schemaField API-only).

Tracker: #19322 / Linear OBS-2085.

cnpierrepapi and others added 6 commits August 21, 2026 19:18
The hand-maintained table was wrong. It described incidentsSummary as the write
gate and said mlModel and mlFeature do not support incidents. Neither is true on
master: the write gate is the IncidentOn relationship in IncidentInfo.pdl, and
writes to those two are accepted and return an incident URN. They simply cannot
be read back.

Three files decide what happens, not one, and they do not cover the same set.
IncidentOn lists ten entity types, seven of those carry incidentsSummary, and
five are given an incidents field by incident.graphql. An entity that clears the
first and fails the others is worse off than one that is cleanly rejected,
because the call succeeds and nothing reports a failure.

The table is now derived from those three files by modeldocgen.py and inlined at
docs build time, so it cannot drift the way the hand-written version did. The
section is rewritten around the three layers, and the quoted rejection message
now names the version it came from rather than being reproduced from memory.

The two frontend copies of the same list are deliberately not derived here. They
live outside the metadata model, and keeping them from drifting is the subject
of datahub-project#19097.
…dback claim

The RaiseIncidentInput block on this page marked resourceUrn as required and
omitted resourceUrns entirely, so a multi-resource incident could not be
constructed from the shape shown. On master resourceUrn is optional and
resourceUrns exists, with each required only when the other is absent.

Saying nothing reads the incident back was also too strong. Incident implements
Entity, so the URN returned by the mutation resolves through the generic
entity(urn:) query. The gap for mlModel and mlFeature is on the asset side:
neither can list its own incidents and neither renders them.
…ypes

The generated table claimed "Readable in GraphQL" from the SDL alone. That is not
what the SDL decides. A type can declare the incidents field and still have no
resolver behind it, in which case the query is valid and the field comes back null,
which reads as an entity with no incidents rather than an unsupported one. Split
into two columns and derive the second from the entitiesWithIncidents list in
GmsGraphQLEngine.java.

Fix a false negative while here. The GraphQL type name was derived by capitalising
the first letter of the entity name, so mlModel became MlModel and matched nothing.
Every ml entity was reported as unsupported at the GraphQL gates even after
datahub-project#19112, datahub-project#19132 and datahub-project#19367 closed them. The ml family spells the prefix ML, so
handle that alongside the existing SchemaFieldEntity override.

Prose updated for master. mlModel, mlFeature and mlFeatureTable are complete, so
the worked example of an accepted write that goes nowhere is now service and
aiAgent, which have no GraphQL type at all. schemaField closes the four backend
gates but has no Incidents tab, so it is called out as API only. The list of
copies living outside the metadata model is now three, adding the MCP
list_incidents fragments from datahub-project#18341.
@cnpierrepapi
cnpierrepapi force-pushed the docs-incidents-supported-entities branch from 6248c1a to 7d35e64 Compare August 21, 2026 18:23
@cnpierrepapi

Copy link
Copy Markdown
Contributor Author

Rebased onto master 11eb9ecf and pushed as 7d35e64e. Clean rebase, no conflicts.

Three things went in beyond the rebase.

The resolver gate is now derived. Column 3 claimed "Readable in GraphQL" and derived it from the SDL alone, which by your own gate 4 rule is not a claim the SDL can support. There are now two columns: incidents field in the SDL, and incidents resolver wired, the second parsed from the entitiesWithIncidents literal in GmsGraphQLEngine.java. The docs say why they are separate, which is that declaring the field without a resolver does not fail, it returns null, so it reads as an entity with no incidents rather than an unsupported one.

The generator had a false negative and I would have shipped it. The GraphQL type name was derived by capitalising the first letter, so mlModel became MlModel and matched nothing in either the SDL or the engine. Every ml entity would have printed No at gates 3 and 4 even though #19112, #19132 and #19367 closed them. It was invisible until mlModel actually became supported, because before that the wrong answer and the right answer were the same word. Fixed by handling the ML prefix next to the existing SchemaFieldEntity override. This is the same class of mistake that killed #19117, so it is worth saying out loud rather than folding into the rebase.

Current output, checked against master by hand:

raise summary SDL resolver
dataset, chart, dashboard, dataFlow, dataJob Yes Yes Yes Yes
schemaField Yes Yes Yes Yes
mlModel, mlFeature, mlFeatureTable Yes Yes Yes Yes
service, aiAgent Yes Yes No No

Prose follows master. The ML paragraph is gone, since the write that looks like it worked and then goes nowhere is now service and aiAgent. Those two are a layer worse than the ML entities ever were: there is no Service or AIAgent type in the GraphQL schema at all, so there is nothing to hang the field on. schemaField is called out as API only, no Incidents tab, as you asked. The "any type absent from the table" line named mlFeatureTable, which is now supported, so that is corrected too.

One scope question rather than an assumption. Gate 7 is not derived. The MCP list_incidents fragments live in datahub-agent-context, outside the metadata model, and modeldocgen.py is a metadata-model generator, so reaching into that package is the same scope line I argued for keeping on the two frontend gates. It is named in the prose alongside them with a pointer to #19097 and #19322. Happy to derive it if you would rather the table carry all five parseable gates, but I did not want to widen the generator's reach without asking.

On verification: I cannot run :metadata-ingestion:lintFix here, no venv and the task assumes POSIX, so CI is the real check on formatting. The table output above came from running the generator functions directly against master. Prettier on docs/incidents/incidents.md is clean.

The generator reads the entitiesWithIncidents list out of it now, so without
declaring it Gradle treats modelDocGen as up to date when only that file changes
and the generated table goes stale. That is the exact drift this table exists to
prevent.
@cnpierrepapi

Copy link
Copy Markdown
Contributor Author

Follow-up on 002b1cab, plus a note on the red check.

The Gradle wiring was wrong in my last push. modelDocGen declares the PDL, the registry and the SDL as inputs, but the generator now also reads GmsGraphQLEngine.java for gate 4 and I did not declare it. So a change that touched only entitiesWithIncidents would leave the task up to date and the table stale, which is the exact drift this table is meant to stop. Declared now.

On ci (3.11, testIntegrationBatch1): it is tests/integration/nifi/test_nifi.py::test_nifi_ingest_cluster, a golden file mismatch. Not mine. The branch touches three files, none of them ingestion source: the docs page, modeldocgen.py, and the modelDocGen task in build.gradle. modeldocgen.py is a docs generator that nothing under src/ imports, and testIntegrationBatch1 is a different task.

That batch is unstable across the repo at the moment rather than on this branch. Looking at the last 25 runs of metadata-ingestion.yml, it is red on master and on at least four other people's branches in the same window, with a different golden file each time (oracle, neo4j, snowplow, git clone, nifi here). Nothing has touched the nifi source or its golden files in four days.

I cannot re-run it, I only have pull on this repo, so it needs someone else's push or a maintainer. Not urgent from my side since it is unrelated to the diff.

@maggiehays maggiehays added needs-review Label for PRs that need review from a maintainer. and removed pending-submitter-response Issue/request has been reviewed but requires a response from the submitter labels Aug 21, 2026
@cnpierrepapi

Copy link
Copy Markdown
Contributor Author

@AdrianMachado checking in on this one. With #19322 settled and schemaField staying API-only, the table this generates is already right, and there's nothing left for it to wait on.

I re-checked it against master today. The four things the generator reads (the IncidentOn list, the entities declaring incidentsSummary, the SDL incidents fields, and entitiesWithIncidents in GmsGraphQLEngine) are identical to the 21 Aug rebase, so the output hasn't moved. GitHub shows it mergeable, no conflicts.

The only thing holding it is the changes-requested review from 10 Aug, which asked for the table to be generated, and that's what it does now. Could you take another look when you get a chance?

def _incident_write_gate() -> List[str]:
"""Entity types accepted by the IncidentOn relationship, in declaration order."""
match = re.search(
r'"entityTypes"\s*:\s*\[(.*?)\]',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This takes the first entityTypes in the file, so a second @relationship above IncidentOn silently replaces the table; anchor it on "IncidentOn".

_INCIDENT_PDL.read_text(encoding="utf-8"),
re.DOTALL,
)
return re.findall(r'"([A-Za-z]+)"', match.group(1)) if match else []

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A missing key yields an empty table with exit 0, and the docs then imply nothing supports incidents; raise ValueError instead, as docgen.py does.

text = _strip_line_comments(_INCIDENT_ENGINE.read_text(encoding="utf-8"))
match = re.search(r"entitiesWithIncidents\s*=(.*?);", text, re.DOTALL)
if not match:
return set()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renaming entitiesWithIncidents silently flips the whole resolver column to “No”; raise when the list isn’t found or is empty.

return _INCIDENT_GRAPHQL_TYPE_OVERRIDES[entity]
if entity.startswith("ml") and len(entity) > 2 and entity[2].isupper():
return "ML" + entity[2:]
return entity[0:1].upper() + entity[1:]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check the derived name against types defined in the SDL or an explicit _NO_GRAPHQL_TYPE = {"service", "aiAgent"} so the next MlModel-style casing bug fails the build.

return set(re.findall(r'"([A-Za-z][A-Za-z0-9_]*)"', match.group(1)))


def generate_incident_support_table(registry_file: str) -> str:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add fixture tests for the six parsers in scripts/tests/test_modeldocgen.py, plus a smoke test asserting the dataset row is all “Yes”.

Comment thread docs/incidents/incidents.md Outdated

{{ inline /docs/generated/incidents/entity-support.md.snippet }}

Any type absent from the table, including `mlModelGroup`, `mlModelDeployment` and

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These hand-picked examples will contradict the generated table if any of them gains support; assert their absence in the generator or drop them.

Entity type for urn: <urn> is not a valid destination for field path: /entities/*
```

`service` and `aiAgent` are the two types where a write can look like it worked and

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hard-codes what the table already shows and will go stale; the _NO_GRAPHQL_TYPE guard above would make the build catch it.

rejection, because the call succeeds and returns an incident URN while the entity
it names never shows the incident.

The table below is generated at docs build time from those four files, so it cannot

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a caveat that the table reflects this docs version and older servers such as v1.5.0.6 support fewer types, since the PR was prompted by mlModel on 1.5.0.6.

The resources that the incident is associated with, for a multi-resource
incident. Must be present and not empty if resourceUrn is not defined.
"""
resourceUrns: [String!]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block still omits id, startedAt, status, priority and assigneeUrns; list all 12 fields or mark the block abridged.

Review on this PR found several ways the generator could publish a wrong
table and still exit 0.

The write gate took the first entityTypes in the file, so a second
@relationship above IncidentOn replaced the table. It now reads the block
that names IncidentOn.

A source that parses to nothing raises instead of printing a column of No.
That covers all four sources, not just the two raised in review.

Derived GraphQL type names are checked against the types defined in the SDL,
so the next MlModel style casing bug fails the build. service and aiAgent are
the declared exception, and that check runs both ways: if either gains a type,
leaves IncidentOn or loses incidentsSummary, the build fails and points at the
docs page that describes them. All *.graphql files are now modelDocGen inputs,
since the check reads them.

Adds fixture tests for each parser, a test that the table generator calls the
guard, and a test against the real sources asserting the dataset row is all Yes.
…n caveat

The input block omitted id, startedAt, status, priority and assigneeUrns.

Drops the hand-picked examples of unsupported types, which would contradict
the generated table if any of them gained support. Notes that the table follows
the docs version and older servers support fewer types. Points the frontend
drift pointer at datahub-project#19629, which superseded datahub-project#19097.
@cnpierrepapi

Copy link
Copy Markdown
Contributor Author

@kyungsoo-datahub thanks for going through this. All nine are in, as two new commits on top so you can see just the changes. I also rebased a copy onto current master and it applied clean with the same tests passing.

Generator:

  • The write gate now reads the block that names IncidentOn, whichever order its keys are in.
  • A source that parses to nothing raises. You flagged two of these and the registry and SDL parsers had the same hole, so all four do it now.
  • Derived type names get checked against the types defined across the SDL, and _INCIDENT_NO_GRAPHQL_TYPE covers service and aiAgent. That check runs both ways. If either gains a type (Service, AIAgent, ServiceEntity all count), leaves IncidentOn or loses incidentsSummary, the build fails and names the docs page. It reads every *.graphql now, so those are modelDocGen inputs too.
  • Fixture tests for each parser, plus the smoke test you asked for against the real repo files.

Docs:

I broke each fix on purpose to see if a test noticed. One didn't: nothing failed if the table generator never called the guard. There's a test for that now, and all 12 breaks get caught.

test_modeldocgen.py passes, and ruff is clean. The real CLI writes the snippet from good sources and exits 1 with no snippet when one is broken. I only dry-ran the Gradle task locally, so CI is the real check on the modelDocGen wiring.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 4 files

View guided diff | Re-trigger cubic

@kyungsoo-datahub kyungsoo-datahub left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with one comment. Please, address it before merging. Thank you.

project.fileTree(dir: "../metadata-events/mxe-schemas/src/", include: "**/*.avsc"),
file(entityRegistry),
file(incidentPdl),
project.fileTree(dir: "../datahub-graphql-core/src/main/resources/", include: "*.graphql"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

documentation.yml doesn’t trigger on datahub-graphql-core/**, so please add it there or a schema-only PR can merge green and break this check for the next unrelated docs PR.

This branch had an error being deployed

1 failed deployment
Preview — 2d06f3e8 Deployed Nov 5, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-contribution PR or Issue raised by member(s) of DataHub Community docs Issues and Improvements to docs needs-review Label for PRs that need review from a maintainer.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants