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

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

Example use within DocAble

Contents
© James C. Davis, 2026–present