Implementation Plan: Post-Convergence Governance & Enforcement
Branch: tier3/governance-enforcement | Date: 2026-09-06 | Spec: spec.md Input: Feature specification from kitty-specs/post-convergence-governance-01M1TMPH/spec.md
Summary
Bring three governance surfaces back into agreement with the code the Convergence (#3881) shipped: (A1) mark four retired-subsystem ADRs Superseded and author one convergence-retirement ADR that also records the client-repo inversion; (A2) name the enforced pair (pyproject [wheel].packages + conftest.landscape/test_layer_rules) as the canonical modularity SSOT, delete the stale self-authoritative ownership manifest and its key-pinning schema gate (D7), and reduce the ownership map to narrative-only; (A3) close the ungated runtime→specify_cli boundary with a shrink-only ledger that exactly mirrors the existing mission_runtime ledger, and refresh _PRODUCTION_ROOTS. Docs + tests only — no runtime code-behavior change.
Technical Context
Language/Version: Python 3.11 Primary Dependencies: pytest, pytestarch (LayerRule), PyYAML (test-side), ruamel not required Storage: N/A (docs + tests) Testing: pytest architectural gates; each new gate carries a red-first non-vacuity test Target Platform: Linux (CI Blacksmith / local) Project Type: single project Performance Goals: each touched gate ≤5 s (suite NFR-002) Constraints: shrink-only ledger (never exact count); enforced pair wins on conflict; no zeitgeist/SaaS redesign epic in this repo (client-repo inversion is binding) Scale/Scope: 4 ADRs edited + 1 new ADR; 2 ownership docs demoted/removed; 3 test files touched; 1 pyproject exclude-list line removed; CLAUDE.md/AGENTS.md + 00_landscape SSOT note
Charter Check
GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.
non-vacuity/red-first test proving teeth. ✅ planned (WP03 tests co-located).
ledger + TestMissionRuntimeBoundary pattern verbatim rather than a new bespoke gate. ✅
(not by suppressing it); _PRODUCTION_ROOTS refresh keeps the retired-import scan non-vacuous. ✅
- ATDD-first / red-first: every gate change (A1 ADR-hygiene test, A3 runtime ledger) ships with a
- Canonical sources, never improvise: A3 mirrors the existing
_MISSION_RUNTIME_ALLOWED_SPECIFY_CLI - Architectural gate discipline: D7 is resolved by deleting the gate with the artifact it pins
- Terminology adherence: ADR + doc prose use canonical terms (Mission, charter/offering). ✅
- Git/workflow discipline: draft PR; operator merges; commit per WP. ✅
No violations → Complexity Tracking empty.
Project Structure
Documentation (this mission)
kitty-specs/post-convergence-governance-01M1TMPH/
├── spec.md
├── plan.md # this file
├── tasks.md
└── tasks/WP01..WP03.md
Source Code (repository root) — files this mission touches
docs/adr/3.x/2026-06-30-1-sync-daemon-identity-and-cleanup-classification.md # A1: status→Superseded
docs/adr/3.x/2026-04-11-1-saas-rollout-and-readiness.md # A1: status→Superseded
docs/adr/3.x/2026-08-09-1-project-sync-store-boundary.md # A1: status→Superseded
docs/adr/2.x/2026-02-27-1-cli-tracker-surface-gated-by-saas-sync-flag.md # A1: status→Superseded
docs/adr/3.x/2026-09-06-1-convergence-retirement-and-client-repo-inversion.md # A1: NEW ADR
docs/adr/3.x/2026-04-25-1-shared-package-boundary.md # A1: LEFT Accepted (precedent)
docs/architecture/05_ownership_manifest.yaml # A2: DELETE
docs/architecture/05_ownership_map.md # A2: reduce to narrative-only
docs/architecture/00_landscape/README.md # A2: name enforced pair as SSOT; client framing
CLAUDE.md (→ AGENTS.md) # A2: name enforced pair as SSOT
pyproject.toml # A2: drop deleted test from ruff-format exclude
tests/architecture/test_ownership_manifest_schema.py # A2/D7: DELETE (pins stale 8 keys)
tests/architectural/test_layer_rules.py # A3: add runtime→specify_cli ledger + tests
tests/architectural/test_shared_package_boundary.py # A3/D6: refresh _PRODUCTION_ROOTS
tests/architectural/test_adr_hygiene_convergence_retirement.py # A1: NEW ADR-hygiene gate + red-first
Structure Decision: single project; changes confined to docs/ and tests/ plus one pyproject exclude-list line. No src/** production logic changes (NFR-002).
Complexity Tracking
No Charter Check violations — section intentionally empty.
Implementation Concern Map
> Concerns are not WPs. tasks translates these into WPs; here IC ↔ WP is 1:1 for clarity.
IC-01 — ADR hygiene & convergence-retirement record (→ WP01)
retirement + client-repo inversion.
2026-04-25-1 (unchanged/verified), new test_adr_hygiene_convergence_retirement.py.
front-matter keys. Mitigate by reading each ADR's actual header first.
- Purpose: Stop the decision record asserting deleted subsystems; add one governed record of the
- Relevant requirements: FR-001, FR-002, FR-003.
- Affected surfaces: the four ADRs (front-matter
status+ a supersession note), the new ADR, - Sequencing/depends-on: none (pure docs + one test).
- Risks: ADR front-matter shape varies (2.x vs 3.x); the hygiene test must parse the real
IC-02 — Modularity SSOT canonicalization & ownership-doc demotion (→ WP02)
the D7 gate that pins its stale keys; keep the map as narrative pointing at the enforced pair.
remove that test's line from pyproject.toml [tool.ruff.format].exclude (guarded by test_ruff_format_exclude_ratchet); rewrite 05_ownership_map.md; SSOT note in 00_landscape + CLAUDE.md; client-repo framing for zeitgeist_client/saas_client.
remove both together. (2) Overlaps Tier-1 PR #3885 which edited the ownership pair; this mission supersedes those edits by deleting the manifest — flag merge-ordering in the PR. (3) A hidden consumer of the manifest → verified: only historical kitty-specs/** + the schema test reference it.
- Purpose: Make the enforced pair the named SSOT; remove the stale self-authoritative manifest and
- Relevant requirements: FR-004, FR-005, FR-006, FR-007; C-002.
- Affected surfaces: delete
05_ownership_manifest.yaml+test_ownership_manifest_schema.py; - Sequencing/depends-on: none.
- Risks: (1) deleting the test while leaving its
excludeentry reds the ruff-format ratchet →
IC-03 — runtime→specify_cli boundary gate + _PRODUCTION_ROOTS refresh (→ WP03)
cover the real roots (D6).
frozenset of the 23 live first-level subpackages + bare-import sentinel, TestRuntimeSpecifyCliLedger with within-ledger / rejects-out-of-ledger / no-stale-entries — reusing the existing _collect_specify_cli_imports / _out_of_ledger_specify_cli_imports / _specify_cli_subpackage helpers); test_shared_package_boundary.py _PRODUCTION_ROOTS.
test_ledger_has_no_stale_entries — never an exact count (C-003). (2) specify_cli.cli/.next must NOT enter the ledger (they stay hard-forbidden by the existing TestRuntimeBoundary); verified the 23 live subpackages exclude them. (3) The bare import specify_cli (2 edges in runtime_bridge_io.py, subpackage "") must be an explicit ledger entry with rationale. (4) Keep runtime cost ≤5 s — the AST scan of src/runtime is small.
- Purpose: Make a new
runtime→specify_cliinversion red CI (#3522); make the retired-import scan - Relevant requirements: FR-008, FR-009; NFR-001, NFR-003; C-003.
- Affected surfaces:
test_layer_rules.py(new_RUNTIME_ROOT,_RUNTIME_ALLOWED_SPECIFY_CLI - Sequencing/depends-on: none (independent of WP01/WP02).
- Risks: (1) the ledger must be shrink-only (allow ≤ current), matching
mission_runtime's
Ledger ground-truth (live AST scan of src/runtime/, 2026-09-06)
93 edges across 23 first-level specify_cli.<sub> subpackages: bulk_edit, coordination, core, events, invocation, lanes, migration, mission, mission_loader, mission_metadata, mission_step_contracts, mission_v1, missions, requirement_mapping, retrospective, review, runtime, shims, status, status_lanes, task_utils, workspace plus the bare-import specify_cli sentinel ("", 2 edges in runtime/next/runtime_bridge_io.py resolving the legacy missions package path). cli/next are absent (they remain hard-forbidden by TestRuntimeBoundary).