Each control declares its substrate assumption as metadata; a query computes the blast radius.

Control↔substrate dependency (computed blast-radius)

Intent — Make each control declare the substrate assumption it bakes in as typed metadata, so "which controls depend on which part of the substrate, and what will break when I change it" is a computed query, not a grep-and-read. Before a cross-cutting substrate change, the static-analysis blast radius is known up front (our instance: each lint that reads the deployment-topology model declares a typed plane-assumption (GKE-only / Cloud-Run-only / plane-aware / plane-agnostic) joined against the topology to print exactly which lints a migration puts in scope).

SummaryEach control declares its substrate assumption as metadata; a query computes the blast radius.
TargetBridge · System models
Formtyped-ir
EnforcementHard (deterministic) — a declaration lint requires every substrate-reading control to declare its assumption; the blast-radius table is computed from those declarations. Lands audit-only, then promotes to blocking

Motivation — the failure it kills

A control (a lint, a gate, a validator) usually bakes an assumption about the substrate it checks against: "a service is a Kubernetes Deployment under this directory," "the scaler exposes this method," "the manifest carries this field." The assumption sits buried in the control's body. It is invisible until you change the substrate.

Then the change lands and the fleet fails in two silent ways at once:

The engineer planning the migration cannot see this coming. "Which of our checks assume the old substrate?" is answerable only by grepping for who imports the substrate model, then reading each body to judge whether it bakes the old shape. The entanglement that decides the whole refactor's blast radius lives in prose and grep: exactly the invariant-that-lives-only-in-code smell, and the failure is near-certain the moment someone migrates a service without knowing which controls will misfire.

Why it's not just "grep for who imports the substrate model"

Grep finds the importers. It does not find their assumption. Two controls can both import the deployment model; one asserts GKE-only facts and will misfire on a migrated service, the other reads only the plane-agnostic name and is safe. Grep cannot tell them apart; you still read both bodies. This mechanism lifts the assumption out of the body into a typed declaration, so the importer and its stance toward the substrate are one queryable fact.

Each neighbour handles a real slice (parity keeps the model honest, read-don't-copy removes value drift), and both are green right up to a migration that then misfires half the fleet. What neither supplies is the control→substrate dependency edge as a first-class, typed, queryable fact, and that edge is what computes the blast radius before you touch the substrate.

Mechanism

The whole thing is stable-lint-reads-declarations: the table is derived from the typed fields at query time, so nothing is generated and no N-row map can drift.

Prerequisites

Consequences & costs

Known uses

Related mechanisms