Contracts

diagram-drift-guard.md

Contract: Schema-diagram drift guard (FR-004, NFR-001, C-003) — hardened per post-plan squad

Both sides of the comparison (architecture HIGH — diagram side was missing)

  • Model side: introspect via an explicit file:class binding table (1:N). Pydantic model_fields with FieldInfo.alias or name + transitive nested recursion; frozen-dataclass fields(); StrEnum via list(). Never a hand-copied count.
  • Diagram side: the @startyaml field-declaration shape is PINNED — top-level YAML keys (recursing into nested-model sub-maps) = the declared field set; scalar example values are excluded. The guard extracts this set and diffs it against the model side.

Non-fakeable tests (reviewer HIGH/MEDIUM)

1. Completeness over ALL kinds: add a synthetic ArtifactKind member → the guard FAILS until it carries an explicit disposition (diagrammed | consciously-omitted) in the binding table. (Not just the 4 priority kinds.) 2. Omit-a-field: a diagram MISSING a model field FAILS (not only "add a field to the model"). 3. Nested depth-2: add a field to a genuinely nested value-object — AgentProfileSchema → AgentSpecialization, or MissionStepContract → MissionStepContractStep → inputs (DRG is FLAT — do not use it for the depth test) — and assert FAIL. 4. Binds styleguides/models.py:AntiPattern vs the DRG anti_pattern NodeKind (no class) correctly.

no-egress-proof.md

Contract: No-egress proof (NFR-002, C-001) — hardened per post-plan squad

Execution locus (architecture HIGH)

  • Python orchestration (fence recovery, html.unescape, SVG injection) runs host-native, stdlib-only (docs-pages.yml has no setup-python / no pip install — no third-party imports).
  • Only the untrusted java -jar plantuml.jar invocation is wrapped: docker run --network=none -v <tmp>:<tmp> <digest-pinned-JRE-image> java -jar plantuml.jar …. The JRE image is prefetched before the isolated run. Drop host setup-java (redundant).

Proof (both required, BLOCKING CI gates)

  • (a) Behavioral SANDBOX (reviewer MEDIUM): a diagram with !includeurl pointed at a local listener renders under SANDBOX and the listener sees zero inbound connection (or the SANDBOX-specific refusal signal). NOT the weak "build fails" disjunct.
  • (b) Network isolation (reviewer HIGH): the isolation test renders the actual authored schema-diagram corpus (not a sample) under docker run --network=none, and passes. This IS the hard gate (not the ≤60s budget).

Spike (blocking WP01 — planner/architecture HIGH)

Runnability is currently UNPROVEN. WP01 renders a real @startyaml diagram under --network=none on both ubuntu-latest and blacksmith-4vcpu-ubuntu-2404, confirming no font/DNS-driven failure. Its green exit-criterion gates every render/diagram WP. URL-grep is a secondary lint.

plantuml-render.md

Contract: PlantUML render step (FR-001, NFR-003/004/005) — hardened per post-plan squad

Placement

  • Inserted immediately after glossary_linker, before redirect-stub generation + seo_verify --strict, in BOTH docs-build-pr.yml + docs-pages.yml (+ the deploy paths: enumerated allowlist — it is not a glob). A round-trip test runs the full downstream chain over an SVG-injected page.
  • Recovers `plantuml fences from _site: html.unescape the payload; confirm the emitted fence class against real _site HTML (language-plantuml vs lang-…) — the round-trip test is the backstop.

Alt-text (NFR-005, reviewer MEDIUM — concrete predicate)

  • Alt/aria derived from the diagram's title/caption; test renders two differently-titled diagrams and asserts distinct alt equal to the derived caption and NOT in a generic-fallback set ({"yaml","diagram",""}). Confirm the derivation source (PlantUML title vs surrounding markdown heading).

Other testable guarantees

  • Mermaid untouched; malformed fence fails-closed; jar sha256 mismatch fails before render; ≤60s is a monitored budget (not a gate).