Inspiration

Freshness, volume, schema, per-column drift. Every signal a data observability platform reports is per-feed, one asset at a time. None can express a statement about feeds jointly: this model now fails only if these three break together.

Reliability engineering has a name for the case that sentence describes. Redundancy protects against independent failures; a common cause defeats every redundant path at once, which is why NASA's reliability guidance holds that no amount of redundancy pushes total failure probability below the common-cause probability. The ML version is mundane. One refresh job dies and three correlated features go stale together. Meta's "Moving Fast With Broken Data" (arXiv:2303.06094) reports that corrupted partitions are near-ubiquitous at scale, and that their detection system organizes around groups of correlated features for exactly this reason.

DataHub already stores the lineage that says which feeds a model depends on. Does that let us compute something about their joint failure behavior that nobody currently measures?

What it does

The primitive. Probe the model each period with a fixed upstream perturbation and take the Mobius (Harsanyi) transform of the response. The largest non-zero dividend is the interaction order; its support is the minimal failure set, the smallest group that has to fail together to move the metric. The probe never moves a marginal distribution, so a per-feature drift monitor sees a flat line by construction and any movement in the order is structure rather than drift.

That yields an ablation-blindness bound. A method probing only coalitions smaller than the interaction order cannot identify the minimal failure set, because that information is absent from the coalitions it examined. Single-node ablation is blind at order 2 and above, pairwise at order 3 and above. This follows directly from the Harsanyi decomposition and is not a theorem I proved. What I contribute is stating it as a bound on debugging methods, computing it from catalog lineage, and writing it back as a certificate.

Proactive: monitoring. I run it on the NSW electricity market, Elec2, 45,312 half-hourly records from 1996 to 1998, the standard real-world concept-drift benchmark. Interaction order sits at 2 early and settles at 3 from period 5, as the Victoria demand feed joins the NSW price and demand pair in the minimal failure set. That matches the physical coupling between the two markets, and I excluded the interconnector column from the player set so the result could not be circular. The certificate goes onto the model in DataHub: order, minimal failure set, document, tag, banner. Nothing has broken. The graph now records that a common-cause failure of those three feeds would be unattributable by one-at-a-time debugging.

Reactive: root cause. When a model degrades silently, a four-node mesh walks lineage, computes blast radius, and root-causes the failure using distributional drift metrics for simple cases and exact interventional Shapley over the lineage graph, with a solo-impact-personalized PageRank prefilter, for multi-cause ones. It writes an RCA Context Document, an incident, a typed culpritChangeType property and an at-risk banner back through MCP. A second agent investigating a model that shares the culprit dataset inherits the finished RCA off the graph rather than deriving it again. Write-back is idempotent: re-running on the same unfixed failure updates one incident, one document and one banner.

How I built it

  • Deterministic core in Python. attribution.py does interventional, baseline-substitution Shapley over a networkx lineage DAG with an impact-aware PageRank prefilter. interaction.py computes the Mobius spectrum: interaction order, minimal failure set, blindness certificate, and the Shapley-equals-Mobius consistency check. drift.py holds the scipy distributional metrics, benchmark_suite.py the multi-baseline harness. LangGraph orchestrates the base workflow. 130 tests, deterministic plus live-DataHub integration.
  • DataHub Core (OSS) via datahub docker quickstart. Reads and metadata writes flow through the mcp-server-datahub MCP server; per-field statistics come from the datasetProfile aspect, emitted via the SDK and read via GraphQL. Libraries: scipy, numpy, networkx, scikit-learn, hypothesis. The Anthropic SDK is used for the baseline arm only.

I also ran the whole thing against lineage I did not author, because I built the graph I then read and that invites the obvious objection.

python -m sentinel_mesh.feast_source --build      # Elec2 -> a real Feast repo
cd feast_repo && feast apply                      # register the feature store
datahub ingest -c feast_repo/datahub_ingest.yaml  # DataHub's certified `feast` connector
python -m sentinel_mesh.demo_feast                # monitor + certify

DataHub's own Feast source ingests the registry on its own terms: entity to MLPrimaryKey, fields to MLFeature, feature view to MLFeatureTable, parquet to a source Dataset, with table-level lineage. Sentinel Mesh reads the feeds back out of the catalog to build its player set, probes each through Feast, and writes the interaction certificate onto that same ingested feature view. Point it at a different feature view and it monitors that one. Nothing is hardcoded.

This also closes a limitation I had been listing. Coalition attribution has to score the model on counterfactual upstream states, and not every team can replay a model on demand. A feature store already answers this. Feast's get_historical_features performs a point-in-time join, returning each feed's value as of a given moment, so "this feed stopped refreshing 24 hours ago" is one retrieval. The counterfactual is served by the same API that builds training sets.

Challenges I ran into

  • Getting the math honest. Correlated upstreams break conditional Shapley, so I use interventional (baseline-substitution) Shapley (Janzing 2020). I do not claim causal identification from observational data. The output is counterfactual attribution under an assumed, catalog-given DAG plus mechanism independence.
  • Avoiding a rigged benchmark. My first multi-cause scenario was additive and the LLM tied me. Rather than bury it I reported it and went looking for where Shapley genuinely wins, which is interaction effects. I also removed a circularity risk by defining ground truth structurally from the model's terms rather than from my own method.
  • Trusting writes I had not read back. Verifying artifacts by reading them out of GMS, rather than trusting the write path, surfaced roughly a dozen defects: duplicate incidents, an unpaged relationship query, tombstoned edges, and a fix diff that proposed breaking the column it had just diagnosed.
  • A bug only real data could expose. On an analytic score function, non-interacting Harsanyi dividends are exactly zero. On a finite holdout they never are: accuracy is quantized at 1/n_eval, so a single flipped prediction creates a spurious top-order dividend and inflates the reported order to the maximum the player set allows. I now floor dividends below a resolution threshold. That bug would have shipped, confidently, on synthetic validation alone.

Accomplishments that I'm proud of

The benchmark, and how to read it

Every arm gets the same model as a queryable function and the same before-and-after data. I report a frontier LLM (Claude Sonnet) and four deterministic baselines: random, drift-magnitude, single-node ablation, and pairwise ablation, which is the escalation a competent engineer actually runs. Ground truth is structural, taken from the model's own terms rather than from my method. Five seeds, with confidence intervals.

Regime single-node ablation pairwise ablation mesh (coalition Shapley)
Additive multi-cause (n=50) 1.00 1.00 1.00
Redundant-OR, 2-way (n=30) 0.00 1.00 1.00
Shared-upstream, 3-way (n=30) 0.00 0.00 1.00

On additive failures everyone ties. Naming a single additive culprit is commoditized and I claim nothing there.

On the 2-way redundant case, single-node ablation is fooled: it names a minor node every time, and the LLM follows it at roughly 0.37 confidence. But an engineer running pairwise reversions recovers the pair and ties me. My advantage in that regime is determinism and exact per-node credit, not correctness.

The 3-way case is where the graph becomes load-bearing. One refresh job fails and three correlated features go stale together, so no single reversion and no pair reversion restores the metric. Single and pairwise ablation both score 0.00, and only coalition Shapley over the lineage graph is correct.

On real data, and what it does not prove

That table is synthetic on purpose, because grading correctness requires exact ground truth. So I also ran the monitor where there is none: Elec2, real market history, no labels for what "should" have been found.

The two claims stay separate because they are different claims. Real data carries the monitoring claim. Interaction order is a measurable, non-trivial quantity that moves on actual market history, and no shipping monitor reports it. The synthetic 3-way regime carries the correctness claim, because it is the only place pairwise ablation scores 0.00. On Elec2, repairing the best pair recovers 97% of the metric gap (+0.1024 of +0.1059). The interaction there is real but graded, so pairwise ablation remains useful on that data and I do not claim otherwise.

Retrieval through Feast and the standalone numpy path independently reach interaction order 3 with the same minimal failure set (nswprice, nswdemand, vicdemand). Two unrelated retrieval mechanisms agreeing on the structure is better evidence that the quantity is real than either run alone, and I did not engineer that agreement.

Certifying the shape of the failure

Exact Shapley already builds the full table of coalition values, the metric under every combination of upstream changes. A second, nearly free linear projection of that same table, the Mobius transform, yields three things:

  • Interaction order: the degree of the failure's non-additivity. Additive is 1, a redundant pair is 2, a three-way common cause is 3.
  • Minimal failure set: the smallest set of upstream datasets you must fix together. {A, C} for the 2-way, {A, C, E} for the 3-way.
  • An ablation-blindness bound: a method testing only combinations smaller than k cannot identify a degree-k minimal failure set. Single-node ablation is blind to every real 2-way, pairwise to every 3-way. This is a consequence of the Harsanyi decomposition, not a new result of mine.

So "single and pairwise ablation both fail on the 3-way case" stops being an observation about two scenarios and becomes structural: the failure carries a degree-3 dividend, and no sub-3 method can see it. I cross-check by verifying that the Shapley values and the Mobius dividends are the same object viewed two ways; they agree to 1e-9. The agent then writes interactionOrder and minimalFailureSet onto the at-risk model as queryable structured properties, so the next agent inherits how many things must be fixed together and which ones.

The underlying math is established (shapiq, Faith-Shap, Shapley-Taylor, Janzing, do-Shapley). What I found no prior art for is applying interaction-order certification to data-catalog lineage.

Open-source contributions

Four pull requests are open upstream, filed as esbuilds:

  • Interaction certificate RFC (datahub#18975) proposes interaction order and minimal failure set as a first-class aspect on mlModel and mlFeatureTable, so any producer can write it and any consumer can act on it.
  • get_dataset_profile (mcp-server-datahub#142), a read tool for per-field profile statistics, which the MCP server did not expose. It powers my drift and attribution steps.
  • ml-incident-root-cause (datahub-skills#44), a skill for ML-lineage root-cause investigation.
  • ml-dependency-fragility (datahub-skills#108), a skill for the proactive half: measuring and certifying interaction order.

I built a fifth, a raise_incident mutation tool, then found upstream PR #137 already adding it and did not file mine. Searching the target repo's open PRs before writing turned out to be the cheapest lesson of the week.

Also built, verified on live OSS Core v1.5.0.6

  • Blast radius and idempotent retrain metadata. From a culprit dataset I traverse scrollAcrossLineage(DOWNSTREAM), rank at-risk models by lineage degree, and write an idempotent retrainRecommended structured property plus an at-risk banner and a deduped incident. The structured property is the canonical record; the incident is best-effort, since OSS has no incident externalId and dedup is therefore subject to indexing lag.
  • Target-leakage detection via temporal lineage. A feature is leaky if any lineage ancestor was refreshed after the prediction cutoff, using Operation.lastUpdatedTimestamp over multi-hop lineage. It flags the temporally invalid ancestor that a stats-only or snapshot-only detector cannot see, with false-positive controls: temporal-only, unknown-is-not-leakage, whitelist.
  • Deterministic retrain trigger. Dual-gated on a Page-Hinkley change point in the impact series and sustained impact over k windows, so drift alone never triggers a retrain.
  • Attribution variants. Asymmetric (lineage-ordered) and Grouped/Shapley-Owen, plus a fixed-seed sampling fallback for wide lineages. I declined Causal Shapley: it requires mechanism trust I do not have.
  • Agent Context Kit integration for context assembly (get_dataset_queries) alongside my MCP action tools. ACK for reads, MCP for writes.

What I learned

Determinism and write-back are necessary and unremarkable; every serious entry will have them. The defensible work was finding the regime where the metadata graph changes the answer, and then proving it against a baseline strong enough to win everywhere else.

What's next for Sentinel Mesh

Extend the benchmark taxonomy to schema drift and label drift, add a --datahub-mode so MCP and ACK agents can be scored on live lineage, and follow the RFC through review toward an aspect any producer can write.

Built With

Share this project:

Updates