Implementation Plan: Charter kind/tier vocabulary cutover closeout
Branch: feat/charter-kind-tier-vocab-closeout | Date: 2026-10-09 | Spec: spec.md Input: Mission specification from kitty-specs/charter-kind-tier-vocab-closeout-01M4H5RF/spec.md
Summary
Finish the single-authority kind/tier vocabulary left by the merged doctrine→charter cutover and clean its inert residue. Five converging strands: (1) fix the facade re-export gate's blind spot for non-class values so a drifted tuple/frozenset cannot cross a facade unseen (#5836 — the enabler); (2) teach the kind-vocabulary gate the five mirror shapes it misses and migrate every live site to derive from ArtifactKind (#5823); (3) retire the transitional ArtifactKind.core in favour of a has_layered_repository capability predicate, widening the diagnostic surfaces to glossary_pack/skill/asset (#5824); (4) unify the pack-tier token under one kernel authority with an empty-allowlist gate (#5825/#5961); (5) delete the 13 dead spec-kitty-standalone.md copies, leaving graph.yml per its existing decision (#5322).
The whole mission is an exercise of single canonical authority: every new kind/tier fact derives from ArtifactKind or the new kernel tier authority — no hand-copied mirror, gates close with empty allowlists.
Technical Context
Language/Version: Python 3.11+ Primary Dependencies: typer, rich, ruamel.yaml (no new dependencies) Storage: N/A (source + test + generated-copy files) Testing: pytest; targeted blast radius per CLAUDE.md — tests/charter/, tests/charter_offering/, the specific named architectural gate files, and the owning module trees; make test-fast baseline. No full tests/architectural//test-full sweep (NO_FULL_HEAVY_SUITES_IN_MISSION). Target Platform: Cross-platform CLI Project Type: single (library/CLI) Performance Goals: N/A (no runtime hot path touched) Constraints: empty-allowlist gates; ruff+mypy --strict clean; on-disk packs/built-in/ and _BUILT_IN_DIR_NAME unchanged; no persisted provenance value broken; graph.yml untouched. Scale/Scope: ~6 architectural-gate and authority files + ~6 kind-mirror sites + ~41 tier-token sites + 13 deletions.
Charter Check
GATE: Must pass before research. Re-check after design.
- Single canonical authority (DIRECTIVE_044) — PASS by design: this is the mission's whole point. Every fact derives from one owner.
- Architectural gate discipline (DIRECTIVE_043) + empty-allowlist ratchet (ADR 2026-09-30-1) — PASS: every gate extended/added closes empty; each rule has a non-vacuous self-mutation test; a gate-unmask cannot self-validate.
- Test remediation / red-first (DIRECTIVE_041, C-011 ATDD) — PASS: each gate rule lands its failing case first; no skip/disable/xfail/retry-to-green.
- Smallest viable diff + locality (DIRECTIVE_024/025) — PASS with a hard out-of-scope fence (#5323, #4400, #5959, #5960, #5826).
- Canonical sources + terminology (DIRECTIVE_044) — PASS: no
doctrine-tier vocabulary reintroduced; terminology guard run. - Decision documentation (DIRECTIVE_003) — PASS: four material scope decisions recorded as resolved Decision Moments; the
coreretirement, surface-#4 widening, tier-spelling direction and graph.yml call captured.
No violations → Complexity Tracking empty.
Project Structure
Documentation (this mission)
kitty-specs/charter-kind-tier-vocab-closeout-01M4H5RF/
├── spec.md # done (committed e7763f3)
├── plan.md # this file
├── checklists/requirements.md
└── tasks/ # WP outline + packages (/spec-kitty.tasks)
Source Code (repository root)
src/charter/offering/artifact_kinds.py # authority: add has_layered_repository; delete core/_CORE_KINDS/CORE_KIND_PLURALS
src/charter/offering/packs/pack_manifest.py # R1′ RECOGNISED_ARTIFACT_DIRS
src/charter/offering/packs/pack_assembler.py # R4 _ARTIFACT_DIRS_AND_GLOBS
src/charter/activation/context_renderers/fetch_stanza.py # R1′ _VALID_SELECTOR_KINDS
src/charter/activation/consistency_check.py # R5 _CLI_KIND_TO_DRG_SINGULAR
src/charter/activation/synthesizer/{project_drg.py,topic_resolver.py} # R6 + SYNTHESIZABLE_KINDS home
src/specify_cli/mission_step_contracts/executor.py # R6 _ARTIFACT_TO_NODE_KIND
src/charter/{bundle.py,...}, src/specify_cli/cli/commands/charter/_synthesis.py # 3-kind subset restatements
src/specify_cli/cli/commands/_charter_pack_collect.py # core surfaces #1+#2 (_ORG_ARTIFACT_DIRS)
src/specify_cli/charter_runtime/lint/checks/org_layer.py # core surface #3
src/specify_cli/charter_packs/sources/api_source.py # core surface #4 (widen)
src/specify_cli/upgrade/migrations/_charter_pack_cutover_snapshots.py # DEFAULT_KIND_GATE isolation
src/kernel/pack_tiers.py (NEW) # pack-tier token authority
src/charter/offering/pack_paths.py, pack_skills/{validation,repository}.py, activation/pack_manager.py, ... # tier-token sites
<13 generated agent dirs>/spec-kitty-standalone.md # delete
tests/architectural/test_charter_kind_vocabulary_single_authority.py # R1′/R4/R5/R6 rules + self-tests
tests/architectural/test_charter_facades_reexport_offering.py # non-class identity coverage + self-test
tests/architectural/test_pack_tier_token_single_authority.py (NEW) # tier-token gate
tests/charter_offering/test_artifact_kinds.py # has_layered_repository unit tests
<targeted tests> for repointed surfaces (collision/lint/api-source)
Structure Decision: single-project library/CLI; single_branch topology — WP01→WP02→WP03 converge on artifact_kinds.py and the facade table, so sequential execution in one checkout avoids self-inflicted lane-merge conflicts. WP04/WP05 are independent but share a few files (pack_manager.py, list_cmd.py, drg/merge.py), sequenced to keep the kind-vocab gates green.
Complexity Tracking
None — no charter violations to justify.
Implementation Concern Map
> Concerns, not work packages. /spec-kitty.tasks turns these into executable WPs.
IC-01 — Facade re-export gate: non-class coverage (#5836)
- Purpose: The facade identity gate silently skips re-exported non-class values (tuples/frozensets lack
__module__), so a drifted kind-vocabulary constant could cross a facade unchecked. Close it so the kind gate's single-authority check is actually enforceable across the facade boundary. - Relevant requirements: FR-001.
- Affected surfaces:
tests/architectural/test_charter_facades_reexport_offering.py(_untabled_reexports+ a new non-class self-test). Test-only. - Sequencing/depends-on: none — the enabler; land first.
- Risks: must resolve a plain value's origin by object-identity scan of
_IDENTITY_REQUIRED_ORIGINSmodules without breaking the existing__module__-based class path; keep the existing planted-wrapper self-test green.
IC-02 — Kind-vocabulary gate: R1′/R4/R5/R6 + synthesizable subset (#5823)
- Purpose: Teach the gate the five mirror shapes it misses and migrate every live site to derive from the authority, keeping the allowlist empty.
- Relevant requirements: FR-002..FR-007.
- Affected surfaces: the gate file (new classifier rules + self-mutation tests + a "leaves legitimate constructs alone" test); live sites
pack_manifest.py,fetch_stanza.py,pack_assembler.py,consistency_check.py,executor.py,synthesizer/project_drg.py; 3-kind restatements (bundle.py,write_pipeline.py,_synthesis.py) ontoSYNTHESIZABLE_KINDS. - Sequencing/depends-on: IC-01 (facade enforcement must be real before trusting facade re-exports as the one authority object).
- Risks: false positives on six legitimate constructs (
DIRECT_WRITE_KINDS, totalPROJECT_KIND_DIRS, str→NodeKind_KIND_BY_LEGACY_FIELD,Literal[...]aliases, callable/NodeKind dispatch tables) — rules must be tightly scoped and proven to leave them alone.RECOGNISED_ARTIFACT_DIRSstays a facade re-export (charter.packs) and must remain tabled.
IC-03 — Retire core via has_layered_repository (#5824)
- Purpose: Replace the transitional
corewith a capability predicate (11 kinds) and widen the four diagnostic surfaces to cover glossary_pack/skill/asset; deletecore/_CORE_KINDS/CORE_KIND_PLURALS+ facade re-export. - Relevant requirements: FR-008..FR-011.
- Affected surfaces:
artifact_kinds.py(new predicate + frozenset; deletions),_charter_pack_collect.py,org_layer.py,api_source.py(widen),_charter_pack_cutover_snapshots.py(isolateDEFAULT_KIND_GATE), facade table +charter.drg__all__. - Sequencing/depends-on: IC-02 (shared
artifact_kinds.py+ facade table; land after the gate rules so a new frozenset is validated by the live gate). - Risks: the fifth
CORE_KIND_PLURALSusage (DEFAULT_KIND_GATE) must NOT get the topology predicate; the newhas_layered_repositoryfrozenset must not trip any IC-02 rule (it is enum-member-valued, no NodeKind values → safe).
IC-04 — Pack-tier token single authority (#5825/#5961)
- Purpose: One kernel-owned pack-tier token (spelling
"built-in"), every site deriving from it, net-new empty-allowlist gate; leave the on-disk directory and_BUILT_IN_DIR_NAMEfixed. - Relevant requirements: FR-012..FR-014, NFR-003.
- Affected surfaces: new
kernel/pack_tiers.py;pack_paths.py/presets.py(PackTier),pack_skills/{validation,repository}.py(Tier,_TIER_RANK),agent_profiles/repository.py(_LAYER_RANK), provenance sites,pack_manager.py_LAYER_SEGMENTS,list_cmd.py; new gatetest_pack_tier_token_single_authority.py. - Sequencing/depends-on: largely independent of IC-01..03; coordinate shared files (
pack_manager.py,list_cmd.py,drg/merge.py) to keep kind-vocab + built-in-location gates green. - Risks: must split tier-sense
"built-in"from the on-disk directory sense; verify no"builtin"provenance string is persisted/compared (snapshots, fixtures, serialized config) before changing it — if any is persisted, that site is excluded with a recorded rationale.
IC-05 — Dead residue cleanup (#5322)
- Purpose: Delete the 13 dead
spec-kitty-standalone.mdgenerated copies (no regeneration path exists); leavegraph.ymluntouched. - Relevant requirements: FR-015, C-004.
- Affected surfaces: 13
*/spec-kitty-standalone.mdfiles.graph.ymlexplicitly NOT touched. - Sequencing/depends-on: independent.
- Risks: confirm no
src/reader and no manifest/ownership contract references the deleted copies;command_installer.verifytreats them as unmanaged orphans (consent-gated), so deletion is the operator-approved remedy.