problemuses, memberOf, layer and context in its own gate.spec:validation.architecture-constraintsPacks: pack:corpus-checks-v1, pack:design-management-v1, pack:self-hosting-v1
Design: 6 design entries · 3 pinned declarations · 1 decision
Findings: no errors, no warnings
specs/validation/architecture-constraints.sdp.md
No open questions and no blocking questions; implementation binding present, verifier binding present, and expected-outcome oracle none.
The original design gave architecture a validation tier of its own. Its rules said that only adapter components depend on external systems, that domain code imports no infrastructure and that layers depend in one direction; dependency-cruiser, architecture tests and lint rules ran them, and their findings joined the one report (docs/lineage/v0-design/06-extraction-and-validation.md §6.4). Since the architectural annotation decision, the data those rules read is in the graph with no inference: a component anchor states its layer and its context, and uses and memberOf edges are anchored. The same decision keeps the check out of the Protocol. A negative architecture constraint stays declared intent in a Spec, and a corpus that wants it enforced checks its declared uses, layer and context in its own gate.
The first adopter checks that a component in its platform context has no uses edge to a component in another context, resolving each end through memberOf. Architecture crossings, recipe 32, reports differences in context or layer and refuses nothing. The architectural annotation decision keeps architecture checks in the corpus's own gate, and the Protocol supplies the reusable graph walk.
The walk returns the edges a rule forbids as corpus findings, so one body answers a reader through sdp q and fails a gate when the corpus names it as a rules file; the first adopter's isolation check becomes the walk named by sdp validate --rules with the isolated context platform, and none of its own code. An edge the walk cannot decide, because a component states no layer or context or an end belongs to no component, is listed apart: a breach the anchors cannot show is a curation gap, not a finding.
problemuses, memberOf, layer and context in its own gate.outcomerule line 28 · specs/validation/architecture-constraints.sdp.md:28sdp validate --rules with the severity the rule states.rule line 29 · specs/validation/architecture-constraints.sdp.md:29constraint Spec that says what is forbidden and why, and keeps the rule's machine form, its forbidden layer pairs and isolated contexts, as data its corpus rule passes to the walk.rule.walk line 30 · specs/validation/architecture-constraints.sdp.md:30uses edge to its component, directly or through memberOf, and returns each edge a rule forbids with both units, both components and the rule.spec:validation.architecture-constraints.forbidden-layer-pair · enabled, spec:validation.architecture-constraints.isolated-context-as-rule · enabled
rule.walkAsRule line 31 · specs/validation/architecture-constraints.sdp.md:31findings, so the shipped recipe file runs unchanged as a rules file under sdp validate --rules, at the severity the corpus passes it.spec:validation.architecture-constraints.isolated-context-as-rule · enabled
rule.forms line 32 · specs/validation/architecture-constraints.sdp.md:32no example exercises it
rule line 33 · specs/validation/architecture-constraints.sdp.md:33rule.unclassified line 34 · specs/validation/architecture-constraints.sdp.md:34spec:validation.architecture-constraints.unstated-layer-unclassified · enabled
rule line 35 · specs/validation/architecture-constraints.sdp.md:35has-verifier states that the check exists while its result stays in the corpus's run.4 unkeyed rules.
A domain member that uses an infrastructure member breaks a forbidden pair · spec:validation.architecture-constraints.forbidden-layer-pair · ready floor ready
Executable Spec
Given a member of a component at the layer {fromLayer: "domain"} in the context {fromContext: "orders"} that uses a member of a component at the layer {toLayer: "infrastructure"} in the context {toContext: "orders"}
When the architecture walk runs {invocation: "through sdp q"} with the forbidden pair domain to infrastructure and the isolated context platform
Then the walk reports {findings: 1} findings with the rule ids {ruleIds: "forbidden-layer"}
Then the walk lists {unclassified: 0} unclassified edges
Then the command exits {exitCode: 0}Given
a member of a component at the layer {fromLayer: "domain"} in the context {fromContext: "orders"} that uses a member of a component at the layer {toLayer: "infrastructure"} in the context {toContext: "orders"}When
the architecture walk runs {invocation: "through sdp q"} with the forbidden pair domain to infrastructure and the isolated context platformThen
the walk reports {findings: 1} findings with the rule ids {ruleIds: "forbidden-layer"}
the walk lists {unclassified: 0} unclassified edges
the command exits {exitCode: 0}Composing needs the script.
The walk named as a rules file fails validation on an isolation breach · spec:validation.architecture-constraints.isolated-context-as-rule · ready floor ready
Executable Spec
Given a member of a component at the layer {fromLayer: "application"} in the context {fromContext: "platform"} that uses a member of a component at the layer {toLayer: "adapter"} in the context {toContext: "orders"}
When the architecture walk runs {invocation: "as a rules file named by sdp validate --rules"} with the forbidden pair domain to infrastructure and the isolated context platform
Then the walk reports {findings: 1} findings with the rule ids {ruleIds: "isolated-context"}
Then the command exits {exitCode: 1}Given
a member of a component at the layer {fromLayer: "application"} in the context {fromContext: "platform"} that uses a member of a component at the layer {toLayer: "adapter"} in the context {toContext: "orders"}When
the architecture walk runs {invocation: "as a rules file named by sdp validate --rules"} with the forbidden pair domain to infrastructure and the isolated context platformThen
the walk reports {findings: 1} findings with the rule ids {ruleIds: "isolated-context"}
the command exits {exitCode: 1}Composing needs the script.
A component with no layer leaves its edge unclassified · spec:validation.architecture-constraints.unstated-layer-unclassified · ready floor ready
Executable Spec
Given a member of a component at the layer {fromLayer: "domain"} in the context {fromContext: "platform"} that uses a member of a component at the layer {toLayer: "unstated"} in the context {toContext: "platform"}
When the architecture walk runs {invocation: "through sdp q"} with the forbidden pair domain to infrastructure and the isolated context platform
Then the walk reports {findings: 0} findings with the rule ids {ruleIds: ""}
Then the walk lists {unclassified: 1} unclassified edges
Then the command exits {exitCode: 0}Given
a member of a component at the layer {fromLayer: "domain"} in the context {fromContext: "platform"} that uses a member of a component at the layer {toLayer: "unstated"} in the context {toContext: "platform"}When
the architecture walk runs {invocation: "through sdp q"} with the forbidden pair domain to infrastructure and the isolated context platformThen
the walk reports {findings: 0} findings with the rule ids {ruleIds: ""}
the walk lists {unclassified: 1} unclassified edges
the command exits {exitCode: 0}Composing needs the script.
Given a member of a component at the layer {fromLayer:"domain"|"application"} in the context {fromContext:"platform"|"orders"} that uses a member of a component at the layer {toLayer:"infrastructure"|"adapter"|"unstated"} in the context {toContext:"platform"|"orders"}
When the architecture walk runs {invocation:"through sdp q"|"as a rules file named by sdp validate --rules"} with the forbidden pair domain to infrastructure and the isolated context platform
Then the walk reports {findings:number} findings with the rule ids {ruleIds:string}
Then the walk lists {unclassified:number} unclassified edges
Then the command exits {exitCode:number}walkParams line 38 · specs/validation/architecture-constraints.sdp.md:38params.forbidden, a list of { from, to } pairs of layers from the closed set; params.isolated, a list of context tokens; and params.severity, "error" by default or "warning"; either list may be empty, and a pair or context given twice counts once; a parameter name outside these three returns { unknownParams, accepts } alone, before any other check, as every catalog recipe answers a name it does not take; neither list given, a layer outside the set, a malformed token or another severity returns { diagnostics } with no findings; and either answer fails the file when the walk runs as a rules file.walkAnswer line 39 · specs/validation/architecture-constraints.sdp.md:39{ input: { forbidden, isolated, severity }, totals: { usesEdges, resolved, findings, unclassified }, findings, unclassified } is the answer when every parameter name is known and every value lawful, where resolved counts the uses edges whose two ends both resolve to a component; findings sort by from, then to, then rule id, and unclassified rows by from, then to; recipe 32's answer stays as it is.no citation recorded
walkBreach line 40 · specs/validation/architecture-constraints.sdp.md:40uses edge whose ends resolve to two distinct components breaks the pair { from, to } when the using component states layer from and the used one states layer to, and breaks the isolation of a context when the using component states that context and the used one states another; two units of one component never break a rule, as recipe 32 never counts them as crossing.walkFinding line 41 · specs/validation/architecture-constraints.sdp.md:41{ validatorId, subjectId, relatedId, severity, message, from, to, fromComponent, toComponent, rule }, a corpus finding in the shape spec:validation.corpus-rules#design.ruleFinding pins, where subjectId is the using unit from, relatedId the used unit to, both components are { id, layer, context } with null for an unstated attribute, and validatorId is forbidden-layer with rule: { from, to } the pair it breaks, or isolated-context with rule: { context } the isolated context it leaves.no citation recorded
walkMessages line 42 · specs/validation/architecture-constraints.sdp.md:42forbidden-layer message reads <from> uses <to>: layer <from layer> uses layer <to layer> (<fromComponent id> to <toComponent id>), a forbidden pair, and an isolated-context message reads <from> uses <to>: <fromComponent id> in the isolated context <context> uses <toComponent id> in the context <other context>.walkUnclassified line 43 · specs/validation/architecture-constraints.sdp.md:43unclassified rows { from, to, fromComponent, toComponent, missing } list each anchored uses edge the walk cannot decide while a list is non-empty: an edge with an end that belongs to no component, that end's component null and missing: ["component"], and an edge between two distinct components where either leaves unstated an attribute a non-empty list reads, layer for forbidden and context for isolated, named in missing.no citation recorded
specs/validation/architecture-constraints.forbidden-layer-pair.sdp.mdtest/self-hosting-corpus-checks.test.tsTest anchors: test:protocol.model.planned-architecture.differing-layer-and-uses, test:protocol.model.planned-architecture.matching-design, test:protocol.model.planned-architecture.unrealized-design, test:protocol.validation.architecture-constraints.forbidden-layer-pair, test:protocol.validation.architecture-constraints.isolated-context-as-rule, test:protocol.validation.architecture-constraints.unstated-layer-unclassified, test:protocol.validation.corpus-rules.copies-contain-changes, test:protocol.validation.corpus-rules.error-finding-fails, test:protocol.validation.corpus-rules.malformed-finding-fails, test:protocol.validation.corpus-rules.warning-finding-informs. Specs verified by those bindings: spec:model.planned-architecture, spec:model.planned-architecture.differing-layer-and-uses, spec:model.planned-architecture.matching-design, spec:model.planned-architecture.unrealized-design, spec:validation.architecture-constraints, spec:validation.architecture-constraints.forbidden-layer-pair, spec:validation.architecture-constraints.isolated-context-as-rule, spec:validation.architecture-constraints.unstated-layer-unclassified, spec:validation.corpus-rules, spec:validation.corpus-rules.copies-contain-changes, spec:validation.corpus-rules.error-finding-fails, spec:validation.corpus-rules.malformed-finding-fails, spec:validation.corpus-rules.warning-finding-informs.
Build: CI 391 · Branch: cursor/media-plan-and-articles-09b1 · Commit: 076cd4f1f85640d8236e5cca1bffffa68ab79a33 · Start: 2026-10-31T15:32:20.471Z · Stop: 2026-10-31T15:34:02.073Z
| Name | Suite | Result | Duration |
|---|---|---|---|
| executes the bound example against a fresh world | A rules file that empties its graph leaves the next file's graph whole (spec:validation.corpus-rules.copies-contain-changes) | passed | 21.88 ms |
| executes the bound example against a fresh world | An error from a corpus rule fails validation and leaves the Protocol's line alone (spec:validation.corpus-rules.error-finding-fails) | passed | 4.69 ms |
| executes the bound example against a fresh world | A finding with no subject fails its rules file (spec:validation.corpus-rules.malformed-finding-fails) | passed | 4.13 ms |
| executes the bound example against a fresh world | A warning from a corpus rule informs and passes (spec:validation.corpus-rules.warning-finding-informs) | passed | 3.83 ms |
| executes the bound example against a fresh world | A domain member that uses an infrastructure member breaks a forbidden pair (spec:validation.architecture-constraints.forbidden-layer-pair) | passed | 15.92 ms |
| executes the bound example against a fresh world | The walk named as a rules file fails validation on an isolation breach (spec:validation.architecture-constraints.isolated-context-as-rule) | passed | 4.55 ms |
| executes the bound example against a fresh world | A component with no layer leaves its edge unclassified (spec:validation.architecture-constraints.unstated-layer-unclassified) | passed | 2.97 ms |
| executes the bound example against a fresh world | A realized component that differs from its design gives one row per field (spec:model.planned-architecture.differing-layer-and-uses) | passed | 6.29 ms |
| executes the bound example against a fresh world | A realized component that matches its design gives no row (spec:model.planned-architecture.matching-design) | passed | 17.77 ms |
| executes the bound example against a fresh world | A component design no anchor satisfies is listed as unrealized (spec:model.planned-architecture.unrealized-design) | passed | 6.93 ms |
specs/validation/architecture-constraints.isolated-context-as-rule.sdp.mdspecs/validation/architecture-constraints.unstated-layer-unclassified.sdp.mdfromLayer · "domain" | "application" · declared by a member of a component at the layer {fromLayer} in the context {fromContext} that uses a member of a component at the layer {toLayer} in the context {toContext}fromContext · "platform" | "orders" · declared by a member of a component at the layer {fromLayer} in the context {fromContext} that uses a member of a component at the layer {toLayer} in the context {toContext}toLayer · "infrastructure" | "adapter" | "unstated" · declared by a member of a component at the layer {fromLayer} in the context {fromContext} that uses a member of a component at the layer {toLayer} in the context {toContext}toContext · "platform" | "orders" · declared by a member of a component at the layer {fromLayer} in the context {fromContext} that uses a member of a component at the layer {toLayer} in the context {toContext}invocation · "through sdp q" | "as a rules file named by sdp validate --rules" · declared by the architecture walk runs {invocation} with the forbidden pair domain to infrastructure and the isolated context platform| Example | fromLayer | fromContext | toLayer | toContext | invocation |
|---|---|---|---|---|---|
| spec:validation.architecture-constraints.forbidden-layer-pair | "domain" | "orders" | "infrastructure" | "orders" | "through sdp q" |
| spec:validation.architecture-constraints.isolated-context-as-rule | "application" | "platform" | "adapter" | "orders" | "as a rules file named by sdp validate --rules" |
| spec:validation.architecture-constraints.unstated-layer-unclassified | "domain" | "platform" | "unstated" | "platform" | "through sdp q" |
Oracle binding: none
| Declared value | Examples |
|---|---|
"domain" | spec:validation.architecture-constraints.forbidden-layer-pair, spec:validation.architecture-constraints.unstated-layer-unclassified |
"application" | spec:validation.architecture-constraints.isolated-context-as-rule |
| Declared value | Examples |
|---|---|
"platform" | spec:validation.architecture-constraints.isolated-context-as-rule, spec:validation.architecture-constraints.unstated-layer-unclassified |
"orders" | spec:validation.architecture-constraints.forbidden-layer-pair |
| Declared value | Examples |
|---|---|
"infrastructure" | spec:validation.architecture-constraints.forbidden-layer-pair |
"adapter" | spec:validation.architecture-constraints.isolated-context-as-rule |
"unstated" | spec:validation.architecture-constraints.unstated-layer-unclassified |
| Declared value | Examples |
|---|---|
"platform" | spec:validation.architecture-constraints.unstated-layer-unclassified |
"orders" | spec:validation.architecture-constraints.forbidden-layer-pair, spec:validation.architecture-constraints.isolated-context-as-rule |
| Declared value | Examples |
|---|---|
"through sdp q" | spec:validation.architecture-constraints.forbidden-layer-pair, spec:validation.architecture-constraints.unstated-layer-unclassified |
"as a rules file named by sdp validate --rules" | spec:validation.architecture-constraints.isolated-context-as-rule |
fromLayer: NonefromContext: NonetoLayer: NonetoContext: Noneinvocation: NoneGiven
a member of a component at the layer {fromLayer:"domain"|"application"} in the context {fromContext:"platform"|"orders"} that uses a member of a component at the layer {toLayer:"infrastructure"|"adapter"|"unstated"} in the context {toContext:"platform"|"orders"}When
the architecture walk runs {invocation:"through sdp q"|"as a rules file named by sdp validate --rules"} with the forbidden pair domain to infrastructure and the isolated context platformThen
the walk reports {findings:number} findings with the rule ids {ruleIds:string}
the walk lists {unclassified:number} unclassified edges
the command exits {exitCode:number}Composing needs the script.
test/architecture-recipes.test.ts:10test/self-hosting-corpus-checks.test.ts:346test/self-hosting-corpus-checks.test.ts:353test/self-hosting-corpus-checks.test.ts:360Copyable source; the Studio draws static SVG.
Preferred and chosen syntax: requirementDiagram; exact refines, satisfies and verifies, with claim words in nodes and per-edge comments.
Architectural significance is annotated on the anchor of the unit that realizes it. A code anchor may carry role, the architectural pattern the unit plays; a component: anchor may additionally carry layer, one of edge, application, domain, adapter, or infrastructure, and context, its bounded context. The component: anchor is anchored architecture: it is the declared component, and there is no separate architecture file, because the anchor sits where the component is realized. It is not the original design's declared Component that could exist before code; an unrealized component lives in Specs and never as an invented CodeNode. A role is a free, corpus-owned vocabulary whose taxonomy the census renders with counts from the graph; service, decider, projection, read-model, codec, contract, barrel, and utility are the lineage set, not a closed list. layer is a closed set, and a value outside it is an envelope error; layer or context on a non-component anchor is an envelope error. All three attributes are optional, and omission is lawful.
impl:protocol.architecture-walk-recipe · asserts realization of the shipped architecture walk recipe · test/architecture-recipes.test.ts:10Uses
None.
Runtime observation: not tracked.
Runtime observation: not tracked.
Build attestations: not tracked.
Deployments: not tracked.
Bills of materials: not tracked.
Copyable source; the Studio draws static SVG.
Preferred syntax: flowchart. Chosen syntax: labelled flowchart, preserving exact relations, claims and folds.
Implementing units
None.
Referencing units
None.
Enabled verifiers: test:protocol.validation.architecture-constraints.forbidden-layer-pair
Implementing units
None.
Referencing units
None.
Enabled verifiers: test:protocol.validation.architecture-constraints.isolated-context-as-rule
Implementing units
None.
Referencing units
None.
Enabled verifiers: test:protocol.validation.architecture-constraints.unstated-layer-unclassified
| Relation and direction | Other end | Claim | Reason | Exercised keys |
|---|---|---|---|---|
| decidedBy · outgoing | Architectural significance is annotated where it is realizedspec:decisions.architectural-annotation · resolves | declared | None | None |
| dependsOn · outgoing | Source anchors bind code without carrying intentspec:model.anchors · resolves | declared | None | None |
| dependsOn · outgoing | Corpus-authored rules run on the one validation pathspec:validation.corpus-rules · resolves | declared | None | None |
| refines · outgoing | Corpus-authored rules run on the one validation pathspec:validation.corpus-rules · resolves | declared | the architecture walk is a corpus-selected rule over anchored structure | None |
| refines · incoming | A domain member that uses an infrastructure member breaks a forbidden pairspec:validation.architecture-constraints.forbidden-layer-pair · resolves | declared | None | None |
| refines · incoming | The walk named as a rules file fails validation on an isolation breachspec:validation.architecture-constraints.isolated-context-as-rule · resolves | declared | None | None |
| refines · incoming | A component with no layer leaves its edge unclassifiedspec:validation.architecture-constraints.unstated-layer-unclassified · resolves | declared | None | None |
| verifies · incoming | A domain member that uses an infrastructure member breaks a forbidden pairspec:validation.architecture-constraints.forbidden-layer-pair · resolves | declared | None | walk |
| verifies · incoming | The walk named as a rules file fails validation on an isolation breachspec:validation.architecture-constraints.isolated-context-as-rule · resolves | declared | None | walk, walkAsRule |
| verifies · incoming | A component with no layer leaves its edge unclassifiedspec:validation.architecture-constraints.unstated-layer-unclassified · resolves | declared | None | unclassified |
impl:protocol.architecture-walk-recipe · asserts realization of the shipped architecture walk recipe · test/architecture-recipes.test.ts:10specs/validation/architecture-constraints.forbidden-layer-pair.sdp.mdspecs/validation/architecture-constraints.isolated-context-as-rule.sdp.mdspecs/validation/architecture-constraints.unstated-layer-unclassified.sdp.mdRuntime observation: not tracked.
Run results: not recorded in the graph.
None.
Composing needs the script.