Appendix B - 14. Meta-model consumption discipline (read, don't hardcode)
The Structure of Meta-model consumption discipline (read, don't hardcode) — its shape at a glance:
A consumer reads the model at run or lint time. A ban-lint scans consumer code for an embedded snapshot of a value the model already holds and fails the build, so the query path is the only surviving one.
flowchart LR
M[(Live model)] --> Consumer[Consumer reads at runtime]
Snap[Embedded snapshot] -. banned .-> Consumer
L{{Snapshot-ban lint}} -. fails build .-> Snap
Accessible description: a consumer reads the live model at runtime. A dashed edge marks an embedded snapshot of a model value; the snapshot-ban lint fails the build on it, leaving the runtime query as the only path.
Projected from the catalogue entry system-models / Meta-model consumption discipline (read, don't hardcode).
On this page: Intent · Motivation · Applicability · Structure · Sample Code · Consequences · Example use within DocAble · Related Patterns
Intent
Intent — Consume the models by querying them at runtime, never by embedding a hardcoded snapshot — so a lint, test, or brief always reasons from the live model, and a copied-out value can't drift behind the model it was copied from.
Motivation
The models are only a bridge if consumers read them. The moment a consumer hardcodes a snapshot ("our packages are [A, B, C]" pasted into a lint or test), that copy drifts the instant the model changes, and the consumer keeps passing while reasoning about a stale world. This is the single most common substrate-drift recurrence vector: the model migrates, the copy is left behind, the check now verifies the wrong thing. It recurs at every consumer that reaches for a quick literal instead of a query.
Applicability
- The models are queryable (they exist and have a read path — the query surface).
- A lint that bans re-hardcoding the queryable value, or snapshots creep back in.
- A consumer culture of deriving, not copying.
Structure
The Structure diagram appears at the top of this page.
Sample Code
The consumer derives its answer from the model; a lint bans a literal snapshot of a queryable value. The query keeps one authoritative answer every consumer derives, where a copy mints a private answer that drifts the day the model moves.
import ast, sys
def component_names() -> set[str]:
"""The authoritative answer: derived from the model, never a pasted literal."""
return {c["name"] for c in load_model()} # load_model reads the source-of-truth records
# The ban-lint: a hardcoded list of the queryable value is a finding.
KNOWN_SNAPSHOT = frozenset({"editor", "worker", "web"}) # what the model currently returns
def lint(path: str, source: str) -> list[str]:
findings = []
for node in ast.walk(ast.parse(source)):
if isinstance(node, (ast.Set, ast.List)):
literals = {e.value for e in node.elts if isinstance(e, ast.Constant)}
if literals and literals <= KNOWN_SNAPSHOT and len(literals) > 1:
findings.append(f"{path}:{node.lineno}: embedded model snapshot — query the model")
return findings
if __name__ == "__main__":
hits = [f for p in sys.argv[1:] for f in lint(p, open(p).read())]
print("\n".join(hits))
sys.exit(1 if hits else 0)
Consequences
- Slightly more ceremony per consumer. A query call instead of a literal (the intended trade).
- Runtime/lint-time coupling. The consumer depends on the model being loadable when it runs.
- The ban-lint's accuracy. It must recognise a "queryable value" to flag its snapshot (verify it is built).
Example use within DocAble
- The component-registry and model query tool read at runtime by the lint fleet + dispatch.
- The snapshot-ban lint (fails a test embedding a queryable value); the meta-file-preference rule; the lint-scope-declares-against-the-model rule.
Related Patterns
- Bridge — this is the consumption face: agent mechanisms (dynamic-context-injection, docs-hierarchy) and product lints (coherence-lints, doc-hygiene-lints) all read the models through this discipline rather than copying them.
- Consumer — dynamic-context-injection's forward slicer reads the component-zone model this way.
- See also — query-surface: the canonical query path this discipline uses.