Research: Doctrine Schema Diagrams and PlantUML Rendering (Scope B)

D1 — PlantUML rendering approach (FR-001, NFR-002/003/004)

  • Decision: local build-time plantuml.jar pre-render, a post-processor AFTER glossary_linker in BOTH docs workflows. Recover `plantuml fences from docs/_site (html.unescape the payload; assert language-plantuml class), render under -DPLANTUML_SECURITY_PROFILE=SANDBOX, inject SVG + alt.
  • Egress isolation (post-spec squad BLOCKER): unshare -rn is NOT reliable on Ubuntu-24.04 runners (AppArmor apparmor_restrict_unprivileged_userns=1). Portable default: docker run --network=none (runners have Docker). A plan-time spike confirms on blacksmith-4vcpu-ubuntu-2404 + ubuntu-latest. SANDBOX proven behaviorally (a !includeurl diagram fails-closed), not by flag presence.
  • Alternatives rejected: client-side plantuml-server (egress), .NET DocFX plugin (effort).

D2 — Drift guard (FR-004, NFR-001, C-003)

  • Decision: explicit file:class binding table (1:N). Introspect: Pydantic model_fields with FieldInfo.alias or name + transitive nested recursion; frozen-dataclass fields(); StrEnum list(). Binding-completeness from list(ArtifactKind) + priority list. FAIL on mismatch or unregistered kind. ATDD red-first.
  • Model families (verified): Pydantic-with-aliases (AgentProfileSchema + nested), frozen dataclass (ActionIndex), StrEnum (NodeKind=16, Relation=15, ArtifactKind=12).

D3 — Doctrine filing corrections (C-004, confirmed by doctrine lens)

  • NodeKind=16 (live); action-indexArtifactKind (mission concept — diagram + prose in mission-type-resolution.md); step-contract already documented (augment-only); template audited, kept as a note; styleguides.AntiPattern ≠ DRG anti_pattern (NodeKind string, no class). Fill only glossary-pack + anti-pattern.

D4 — ADR / R-04 reconciliation (FR-002, C-006)

  • Cite the active plantuml-diagramming toolguide (charter-prose active); position schema diagrams as a NEW genre distinct from C4 zoom; record the accessibility "restate-facts-in-prose → discharged by doctrine-kinds prose" carve-out; state the new lane trades github.com-source rendering for generated fidelity (docsite-only).

D5 — Module READMEs (FR-005, C-005)

  • Pointer-only; explicit module→plan mapping + fallback; precondition: Scope A merged to main; a structural lint (length cap / forbid field-table markers) enforces no-duplication by machine.