ADR-0010/0011: cross-model references and the network boundary - #32
Conversation
… ADR-0011) Design output from a grilling session on #25. Nothing is implemented; both ADRs record intent so the format work has a settled shape to build against. ADR-0010 decides that a model declares a scope slug and references another model's items as slug.Name, resolving against a whole-file copy committed to this repo. An explicit imports: path list locates copies, each file's own scope: owns its slug, and resolution does not recurse. Provenance lives in a header comment rather than in the schema, because an origin: key would put a vendoring concept into the format that every single-model user would meet in the schema reference, and rather than a lock file, because models commonly sit as peer files in docs/ where a root lock would be several directories away. The surface digest covers structure and normative content but not documentation prose. Qualified entity references stay deferred; they already fail schema validation, and lint gains a friendly error ahead of it. ADR-0011 replaces the blanket no-network rule in go-style.md with the property it was standing in for: lint and render never perform network I/O, and the commands that do are named, opt-in, and delegate transport to git/gh rather than implementing it. ADR-0011 lands with TestADR_0011_OfflinePackages, which walks transitive imports of the offline packages and fails on net, net/http or os/exec. os/exec is banned there deliberately: without it the rule could be evaded by shelling out to curl. Verified to fail when it should, by temporarily banning a package that is genuinely in the graph. ADR-0010 has no test yet because none of it is built; the ADR names what will pin it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Joe Beda <joe@stacklok.com>
Review pass on PR #32 returned 2 blocking and 8 should-fix findings. Blocking. ADR-0010 described the freshness check two incompatible ways: as a flag on lint mirroring --completeness error, and as a command under modelith deps. The second is the decision; the flag wording was left behind when the command group was added, and ADR-0011 repeated it. lint never asks whether origin moved. Blocking. A vendored model is not the repo's work, but nothing said so operationally. The Action lints every globbed file with --completeness and optionally renders each one (action.yml:90-99), so a vendored model would turn a user's CI red for gaps in someone else's document. Ownership-scoped diagnostics now skip a file carrying a provenance header. Local imports. A model in the same repo, canonical where it sits, has no origin and no header. Provenance-header presence is the signal that separates a copy from a canonical file, and it does the same job three times: what to verify, what completeness applies to, and whether an unresolvable import is an error. Peers in one repo import each other with no ceremony. Also: go-style.md is actually updated rather than merely declared updated, and ADR-0011 no longer claims precedence over a rule file, which inverted CLAUDE.md; the TestSchemaStructSync claim is corrected, since it does not cover docs/06-schema-reference.md; duplicate and absent scopes among imports are decided; the narrowing of ADR-0002 that lossy imported Markdown represents is stated rather than left implicit; qualified names in prose are deferred explicitly; and ADR-0011 no longer implies its test proves the no-flag claim end to end, since cmd/modelith reaches net through pflag. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Joe Beda <joe@stacklok.com>
render --check has the same shape as the completeness problem: it fails when a model's .md is absent or stale, and a vendored model owes neither -- its rendered form belongs to its home repo. Vendored files are skipped by render --check; naming one explicitly still renders it, which is how a deep link gets a target. The scope-collision text said there was no local remedy, which stopped being true when the same change introduced local imports: two local models collide and you rename one. Only vendored copies of two upstreams that chose the same slug are stuck, because there is no alias layer. Also names lint as where the error is raised. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Joe Beda <joe@stacklok.com>
Review loop recordTwo rounds, per Round 1 — independent reviewer (Opus), full scope2 BLOCKING, 8 SHOULD-FIX, 6 NIT.
F1 was self-inflicted: the flag wording survived the switch to a Round 1 also missed something a human caught — a model in the same repo, canonical where it sits, has no origin and no header. Local imports are now first-class, and provenance-header presence became the single signal doing three jobs: what to verify, what ownership diagnostics apply to, and whether an unresolvable import is an error. Round 2 — delta only (Sonnet)6 fixed, 4 partial, 1 new. Two partials were real design gaps and are now closed:
The remaining partials (which specific checks stop running, where exactly the collision error surfaces) are implementation-time detail, not open design questions. VerificationAll factual claims in both ADRs were checked against source with file:line —
|
Review pass on PR #32 returned 2 blocking and 8 should-fix findings. Blocking. ADR-0010 described the freshness check two incompatible ways: as a flag on lint mirroring --completeness error, and as a command under modelith deps. The second is the decision; the flag wording was left behind when the command group was added, and ADR-0011 repeated it. lint never asks whether origin moved. Blocking. A vendored model is not the repo's work, but nothing said so operationally. The Action lints every globbed file with --completeness and optionally renders each one (action.yml:90-99), so a vendored model would turn a user's CI red for gaps in someone else's document. Ownership-scoped diagnostics now skip a file carrying a provenance header. Local imports. A model in the same repo, canonical where it sits, has no origin and no header. Provenance-header presence is the signal that separates a copy from a canonical file, and it does the same job three times: what to verify, what completeness applies to, and whether an unresolvable import is an error. Peers in one repo import each other with no ceremony. Also: go-style.md is actually updated rather than merely declared updated, and ADR-0011 no longer claims precedence over a rule file, which inverted CLAUDE.md; the TestSchemaStructSync claim is corrected, since it does not cover docs/06-schema-reference.md; duplicate and absent scopes among imports are decided; the narrowing of ADR-0002 that lossy imported Markdown represents is stated rather than left implicit; qualified names in prose are deferred explicitly; and ADR-0011 no longer implies its test proves the no-flag claim end to end, since cmd/modelith reaches net through pflag. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Joe Beda <joe@stacklok.com>
Design output from a grilling session on #25. Nothing is implemented — these are forward-looking decision records plus one guard test.
ADR-0010 — cross-model references by vendoring
A model declares a
scope:slug and references another model's items asslug.Name, resolving against a whole-file copy committed to this repo.imports:is a list of paths; each listed file's ownscope:owns the slug, so nothing is repeated and nothing can disagree. Resolution does not recurse.origin:key would put a vendoring concept into the format that every single-model user meets in the schema reference; a root lock file would sit several directories from models that commonly live as peers indocs/. The format already carries machine-read header comments (# yaml-language-server:).description,definition,noteand other documentation, so upstream prose polish doesn't page anyone.--completeness errorso posture is chosen per repo.modelith deps:import,update,check. Origins are assumed to be GitHub.ADR-0011 — the network boundary
Replaces the blanket "no network calls" rule in
go-style.mdwith the property it was standing in for:lintandrendernever perform network I/O, and commands that do are named, opt-in, and delegate transport togit/ghrather than implementing it. Split out of ADR-0010 because it governs decisions well beyond imports.Test
TestADR_0011_OfflinePackageswalks transitive imports oflint,render/*,schemaandmodel, failing onnet,net/httporos/exec. Banningos/execis deliberate — without it the rule could be evaded by shelling out tocurl. Verified to fail when it should, not merely to pass today.ADR-0010 has no test because none of it is built; it names what will pin it.
Notes for review
task checkis green. The one//nolint:gosecis the first in the repo, documented pergo-style.md's carve-out for false positives.docs/06-schema-reference.mdandgo-style.mdare deliberately untouched — they are test-coupled and move together at implementation time.Refs #25
🤖 Generated with Claude Code