Implementation Plan: Doctrine Governance Fidelity
Branch: mission/doctrine-governance-fidelity | Date: 2026-06-27 | Spec: spec.md Input: Feature specification from kitty-specs/doctrine-governance-fidelity-01KW42KY/spec.md
Summary
Close three doctrine-governance "signal present, consumer doesn't read it" defects as file-disjoint lanes: (A) interpolate the documentation_policy interview answer into the generated charter (#2153); (B) make org / extension doctrine-pack agent profiles visible to dispatch routing, to --profile governance context, and to agent-profile projection by wiring org_dirs through a single canonical resolver, guarded by an anti-regression gate (#2156 + #2166); (C) promote the built-in-override adjudication from test code into production and wire it into doctor doctrine, then retire the override-policy dead-symbol allowlists and lower the category_7 baseline 7 → 6 (#2082). Approach validated by a pre-planning adversarial squad (4 lenses).
Technical Context
Language/Version: Python 3.11+ Primary Dependencies: typer, rich, ruamel.yaml; internal doctrine., charter., specify_cli.*, mission_runtime packages Storage: Filesystem — .kittify/config.yaml (org packs), .kittify/doctrine/replaceable-builtins.yaml, .kittify/charter/interview/answers.yaml, kitty-specs/<mission>/ Testing: pytest (tests/ unit/contract/integration), plus tests/architectural/ gates (dead-symbol, dead-module, ratchet baselines) and the terminology guard; live-evidence repro via scratch projects with real-format org packs Target Platform: CLI (Linux/macOS), deployed to consumer repos via spec-kitty init/upgrade Project Type: single (Python CLI) Performance Goals: N/A — correctness/fidelity mission; no hot-path changes Constraints: ruff + mypy zero issues, complexity ≤ 15; no built-in-only regression (NFR-001); live-evidence proof for Lane B (NFR-002); fail-closed governance reads (NFR-004); topology: lanes, no coordination branch Scale/Scope: 3 lanes, 4 issues (#2156/#2166/#2153/#2082) + 1 folded layout split-brain (FR-013, to file), 9 implementation concerns; ~8–12 source files + tests; one new architectural gate; one baseline shrink; charter-activation-gated org overlay
Charter Check
GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.
dispatch/context/projection ONLY through the charter activation filter (PackContext.activated_agent_profiles); reuse build_activation_aware_doctrine_service, never splice raw org_dirs. Verified by the architectural-alignment squad (unanimous, live-proven) — original raw-org_dirs plan rejected. ✅ revised.
service, and the existing _doctrine_collect DRG load; no hand-rolled org-root resolution or new DRG plumbing. ✅ planned.
≤ 15, focused test per new branch/helper. ✅ enforced per WP.
self-mutation test; FR-011 allowlist shrink is shrink-only and paired with a full tests/architectural/ dry-run pre-PR (C-004, gate-unmask-cannot-self-validate). ✅ planned.
- Charter is the org-resolution entry point (C-008): org profiles reach
- Canonical sources (C-006): reuse
resolve_org_roots, the activation-aware - Quality gates (NFR-003): ruff + mypy clean on new/touched code, complexity
- Architectural-gate discipline: FR-008 new gate carries a concrete floor +
- Terminology canon: doctrine/prose touches run the terminology guard pre-push. ✅.
- No charter violations requiring justification. Complexity Tracking below is empty.
Project Structure
Documentation (this mission)
kitty-specs/doctrine-governance-fidelity-01KW42KY/
├── spec.md
├── plan.md # this file
├── research.md # Phase 0 (brownfield findings — squad-sourced)
├── issue-matrix.md # ticket dispositions + claims
├── traces/ # tooling-friction / approach / design-decisions
└── tasks.md # Phase 2 (/spec-kitty.tasks)
Source Code (repository root)
src/
├── charter/
│ └── compiler.py # IC-01 (Lane A) — directive interpolation
├── doctrine/
│ ├── agent_profiles/repository.py # org_dirs layer (read; reference)
│ └── drg/
│ ├── org_pack_config.py # resolve_org_roots (canonical helper source)
│ └── override_policy.py # IC-06 — promote adjudication into production
└── specify_cli/
├── invocation/registry.py # IC-03 (Lane B) — dispatch routing org visibility
├── tool_surface/profiles/projection.py # IC-04 (Lane B) — projection org visibility (#2166)
├── charter/context.py (or charter/context) # IC-03 — governance-context org visibility
└── cli/commands/
├── doctor.py / _doctrine_collect.py # IC-07 (Lane C) — doctor doctrine wiring
└── (shared org-profile-dir resolver) # IC-02 — canonical seam (placement TBD in tasks)
tests/
├── architectural/
│ ├── test_no_dead_symbols.py # IC-08 — remove _CATEGORY_C override entries
│ ├── test_no_dead_modules.py # IC-08 — remove _CATEGORY_7 module entry
│ ├── _baselines.yaml # IC-08 — category_7 7→6
│ ├── test_builtin_override_policy.py # IC-06 — re-point imports to production
│ └── (new) test_org_profile_construction_gate.py # IC-05 — FR-008 anti-regression gate
├── integration/ + contract/ + unit/ # per-lane red-first + regression tests
Structure Decision: Single Python CLI project. Three file-disjoint lanes map onto the existing package layout; the only new file is the FR-008 architectural gate. The canonical org-profile-dir resolver (IC-02) is a small shared helper whose final module placement is decided in /spec-kitty.tasks (candidate: alongside resolve_org_roots or a specify_cli profile-resolution helper) — it must not create a cycle.
Complexity Tracking
No Charter Check violations — section intentionally empty.
Implementation Concern Map
> Concerns, NOT work packages. /spec-kitty.tasks translates these into executable WPs.
IC-01 — Charter documentation-policy interpolation (Lane A)
- Purpose: Render the operator's
documentation_policyanswer into the generated charter directive instead of a hardcoded string. - Relevant requirements: FR-001, FR-002.
- Affected surfaces:
src/charter/compiler.py(directive builder, ~line 942-944). - Sequencing/depends-on: none (self-contained).
- Risks: must preserve the empty-answer branch; mirror the
risk_boundariesshape exactly; single sink (charter.md Project Directives) — nodirectives.yamlripple.
IC-02 — Charter-activation-aware org-profile resolver (Lane B foundation)
- Purpose: One helper returning the charter-activated org-pack profiles as a provenance-bearing record
list[ResolvedOrgProfile]({profile, source_layer, source_path}) — NOT a barelist[AgentProfile](which carries no provenance; post-tasks squad found that breaks WP04'ssource_layer="org"). Recovers provenance/source_path from the activation-aware service's inner repository (get_provenance/get_source_path), so every org-honouring consumer resolves the org overlay through the charter gate identically. Composesresolve_org_roots+PackContext.activated_agent_profiles(three-state) — NOT a raworg_dirslist. - Relevant requirements: FR-003, FR-007, C-002, C-006, C-008.
- Affected surfaces: new shared helper (e.g.
resolve_activated_org_profiles(repo_root) -> list[AgentProfile]; placement decided in tasks); reusesdoctrine_service_factory.build_activation_aware_doctrine_service+charter/resolver.pygate. - Sequencing/depends-on: none — foundation for IC-03/IC-04/IC-05.
- Risks: must reuse the existing activation wrapper (never re-implement the gate); preserve provenance so IC-04 can set
source_layer; avoid import cycles; honour theNone-default (all admitted).
IC-03 — Dispatch routing + governance-context org visibility (Lane B)
- Purpose:
ProfileRegistry(routing catalog) and the governance-context repo include the activation-admitted org subset, merged onto their existing project layer (.kittify/profiles) — not by passing raworg_dirsintoAgentProfileRepository. - Relevant requirements: FR-004, FR-005, NFR-002, C-008.
- Affected surfaces:
src/specify_cli/invocation/registry.py,src/charter/context.py(_DEFAULT_AGENT_PROFILE_REPO/ context-building path — note it already has the_build_activation_aware_doctrine_serviceprecedent forcharter context --include). - Sequencing/depends-on: IC-02.
- Risks: routed-but-context-empty half-fix if only registry is fixed; must NOT splice raw
org_dirs(would bypass de-activation); prove with the TWO-regime live assertion (admitted visible / de-activated hidden).
IC-04 — Agent-profile projection org visibility / #2166 (Lane B)
- Purpose:
default_profile_repository()/projection emits the activation-admitted org agents to the host surface + manifest with a non-builtinsource_layer, merged onto the existing project layer. - Relevant requirements: FR-006, C-008.
- Affected surfaces:
src/specify_cli/tool_surface/profiles/projection.py. - Sequencing/depends-on: IC-02.
- Risks: manifest
source_layerprovenance correctness; de-activated profiles must not project; built-in-only repos unchanged.
IC-05 — Activation-bypass architectural gate (Lane B)
- Purpose: Fail CI if a routing/projection surface splices raw
org_dirs/resolve_org_rootsthat bypass the activation filter (i.e. assert the activation-aware seam is used — NOT merely thatorg_dirsis passed, which would certify the bypass). - Relevant requirements: FR-008, C-003, C-008.
- Affected surfaces: new
tests/architectural/gate; scoped allowlist of confirmed built-in-only sites with rationale. - Sequencing/depends-on: IC-03, IC-04.
- Risks: gate must assert the CONTRACT (activation seam) not a fakeable proxy (
org_dirspresence); non-vacuous floor + self-mutation test; do not over-scope to intentional built-in-only sites (agent/tasks.py,charter/contextlanguage cache).
IC-09 — Unify activation-CLI pack layout with runtime resolution (Lane B)
- Purpose: Make
charter activate/deactivate agent-profile <id>resolve org packs from the same layout runtime uses, so activating a runtime-resolvable org profile no longer fails "Unknown agent-profile ID" (folded layout split-brain). - Relevant requirements: FR-013.
- Affected surfaces:
src/specify_cli/cli/commands/charter/_layer_roots.py:24-26(the(org_root / "doctrine").is_dir()gate that rejects flat packs),src/charter/pack_manager.py:_scan_layer_dirs:566-567(org-layer branch scans<pack>/doctrine/<plural>/org/). Reference contract:src/doctrine/drg/org_pack_config.py:resolve_org_roots+DoctrineService._org_dirs(flat<pack>/<plural>/). - Sequencing/depends-on: independent of IC-02..05 but completes the #2156 end-to-end story for activation-list projects; verify before Lane B's two-regime proof.
- Canonical layout (operator-decided 2026-06-27):
<pack>/agent_profiles/(the flat runtime layout) is canonical. The charter activation subsystem is the offender — its<pack>/doctrine/<plural>/org/nesting is the mess to remove (unification, not parity). - Approach (post-tasks squad — BINDING): implement a layout-tolerant resolver — flat preferred, nested fallback — as the DEFAULT, NOT a hard cutover. A hard cutover turns the non-owned
tests/charter/test_pack_manager_catalog.py(≥6 nested fixtures) RED out-of-ownership. Tolerant-default makes flat work (FR-013) while keeping existing nested fixtures green. - Risks: the org-layer scan convention is shared across all org-pack kinds (directives/tactics/styleguides/…). Add regression coverage per kind touched; verify
charter list/activate/deactivatefor ≥2 kinds.
IC-06 — Override-adjudication promotion to production (Lane C foundation)
- Purpose: Move
find_overridden_builtin_urns/find_unsanctioned_overrides/UnsanctionedOverridefromtests/architectural/test_builtin_override_policy.pyintosrc/doctrine/drg/override_policy.py(pure, fail-closed); re-point the test to import from production. - Relevant requirements: FR-009, NFR-004.
- Affected surfaces:
src/doctrine/drg/override_policy.py,tests/architectural/test_builtin_override_policy.py. - Sequencing/depends-on: none — foundation for IC-07/IC-08.
- Risks: must keep
tests/doctrine/test_drg_merge.py+ the override test green; preserve fail-closed semantics.
IC-07 — doctor doctrine override diagnostics wiring (Lane C)
- Purpose:
doctor doctrinereports unsanctioned built-in overrides in a deployed repo (org-packs-present branch), guarded by the existing no-packs short-circuit; project-tier overrides stay ungoverned (documented). - Relevant requirements: FR-010, FR-012.
- Affected surfaces:
src/specify_cli/cli/commands/doctor.py/_doctrine_collect.py(reuse the already-built merged 3-layer DRG); JSON + human emitters;healthy/exit-code path. - Sequencing/depends-on: IC-06.
- Risks: do not introduce new DRG plumbing (C-006); diagnostics only meaningful when
registry.packsnon-empty; don't flip healthy repos to RC≠0.
IC-08 — Dead-symbol allowlist retirement + baseline shrink (Lane C)
- Purpose: Remove the 4 override-policy symbols from
_CATEGORY_C_BUILTIN_OVERRIDE_POLICYand the module from_CATEGORY_7_GRANDFATHERED_ORPHANS; lowercategory_7_grandfathered_orphansbaseline 7 → 6. - Relevant requirements: FR-011, C-004; references #2049 (burn-down item, no sweep).
- Affected surfaces:
tests/architectural/test_no_dead_symbols.py,test_no_dead_modules.py,_baselines.yaml. - Sequencing/depends-on: IC-06, IC-07 (symbols must have a live runtime caller first).
- Risks: gate-unmask cannot self-validate — pair with a full
tests/architectural/(incl. CI-only shards) dry-run before the PR.