Typed frozen-record models + five hand-rolled disciplines; adopt the schema, skip the runtime.

The agent-first MBSE harness

Intent — Build the typed system-models as a thin, hand-rolled harness over plain frozen records: adopt the vocabulary and schema of the right modeling genre per view, but skip its runtime and hand-roll the executable layer. The model then captures your project's own invariants, and a set of build-time checks keeps it equal to the code (our instance: frozen Python dataclasses under five recurring disciplines, not a SysML tool).

SummaryTyped frozen-record models + five hand-rolled disciplines; adopt the schema, skip the runtime.
TargetBridge · System models
Formtyped-ir
EnforcementHard (deterministic) — the harness is construction (typed records); the counted sensors are the per-view drift lints that fail the build when a stored value or a model row diverges from the code

Motivation — the failure it kills

You decide to model your system as executable source-of-truth, and the first instinct is to reach for a real modeling tool: SysML, Alloy, an EMF metamodel. Then two failures land. First, the tool's schema doesn't fit. A real system-model carries project-specific invariants (a verification tier derived from an invariant's temporal shape, a lock-ordering rule, a zone-boundary predicate) that don't map onto any standard library's schema, so you spend more effort fighting the tool's model than building yours. Second, the tool draws or checks; it does not run inside your own lints. A SysML diagram is data a viewer renders; an Alloy model is data a solver checks. Neither ships the discipline that makes the model unable to lie about your code: a build-time check that loads the model, loads the code, and blocks on divergence. The failure is a modeling layer that either doesn't fit your invariants or doesn't wire into your build, so it drifts, and a drifted model is worse than none.

Why it's not just a modeling language, or bare dataclasses

Two adjacent alternatives look like they would do the job. Neither does.

Not a modeling language (SysML / Clafer / Alloy / AADL / EMF). Such a model is declarative data a tool draws or a solver checks, not code your own lints execute. Three things none of them ship:

Not bare Pydantic / dataclasses either. Frozen dataclasses (or Pydantic records) give you validated typed records: the raw material, not the method. This mechanism is the five-discipline layer on top: the look-up seam, derive-and-assert, auto-discovery, the drift-lint, and the query projection. Typed records get you a schema that validates its own shape, and a schema still drifts from the code it describes, because nothing reconciles the two. The five disciplines add that reconciliation: they are what turns a snapshot into a bridge that cannot lie about the code.

Mechanism — five recurring harness disciplines

A fresh project re-instantiates these five. They are what makes the frozen records a bridge rather than a snapshot:

1. The look-up seam. A model field is loaded or derived from the code at build time, never a hand-copied snapshot. If the authoritative states live in a code enum, the model reads that enum and projects it; it does not re-type the names. A hand-copied list is a second source of truth that desyncs the first time someone edits one side. 2. Derive-don't-hand-type + assert-stored-equals-derived. Any field that is a function of other model data (a verification tier derived from an invariant's shape, say) is computed by one derivation function, and a lint asserts the stored value equals the derivation. A hand-typed derived field is a lie waiting to happen: edit the inputs, the stored value goes stale, the model now asserts something false. 3. The auto-discovery loader. Drop a model file into the model directory and it self-registers: the loader discovers it, no central manifest to edit. Clone-and-run, zero ceremony. 4. The drift-lint contract. Per view, one stable check: load the model view, load the code the same way the model does, set-diff, re-run every derived field's derivation, exit non-zero on any divergence. This is the gate that makes the model unable to lie, the counted sensor of this mechanism. 5. The generic query projection. One introspection pass turns any typed-record model into JSON, giving the fleet a read-API over the model without a bespoke serializer per view.

The default posture — hand-roll, and stay portable

Default to hand-rolling. Two reasons. Code is cheap now: a thin harness over frozen records is a few hundred lines an agent writes without complaint. And a real system-model must capture project-specific invariants and properties that don't map cleanly onto any standard library's schema; forcing them into a tool's fixed metamodel costs more than it saves.

The discipline that keeps this from being reinvention: adopt the vocabulary and schema of the best-in-class genre per view (the service-flow view speaks the Backstage service-catalogue entity dialect; the deployment view speaks the topology vocabulary of the deploy manifest), then extend and hand-roll the rest. Because you already speak the standard vocabulary, porting onto a real modeling tool later stays cheap if you ever want the solver or the drawing: you translate a vocabulary you already use, not a bespoke dialect you invented.

The honest scope boundary: package the shared harness, keep the per-view drift lints hand-rolled. The look-up seam, the query projection, the auto-discovery loader factor into a shared library. The drift lints genuinely differ: a component view set-diffs zones, a service view AST-reconciles handlers against specs, a coverage view intersects test coverage with model nodes, a topology view subset-checks the manifest. Forcing those four under one abstraction is over-engineering; leave each hand-rolled.

If you want a real modeling tool

The default is hand-rolling for the reasons above, but these are the genres to adopt vocabulary from, and the optional destinations if you later want a drawing or a solver. Each fits a different view:

Author the views with the shipped scaffolds. This catalogue's companion skill ships a system-models starter kit, a fill-in scaffold set for the 4+1 architectural views (Kruchten): a process-view state-machine model starter, a logical/development-view component-zone model starter, a scenarios-view service-flow model starter (Backstage-dialect YAML + loader), and a physical-view deployment-topology starter. Each scaffold is blank and portable, and each sketches the look-up seam, the derive-and-assert rule, and the drift-lint __main__ this mechanism names.

Prerequisites

Consequences & costs

Known uses

Related mechanisms