Research index: charter-pack-cutover-01M491G6
Phase 0 output. Each decision links to the research file that holds its evidence (file:line) and rejected alternatives. No [NEEDS CLARIFICATION] remains; owner rulings are recorded in spec.md (OD-1..OD-10) and decisions/.
| # | Topic | Decision | Evidence |
|---|---|---|---|
| R1 | Stale default.yaml lists | Frozen per-key snapshots of every released default.yaml (174 releases scanned; 30 ship the file, 5 distinct contents) in original, post-rtk, post-rc5 and post-rtk+rc5 forms; per-key, order-insensitive, id-normalised equality (DIRECTIVE_NNN ↔ numbered stem, static map). | research/default-yaml-snapshots.md, .yaml |
| R2 | Released minimal kind gate | Every released minimal.yaml sets activated_kinds: [directives, tactics]; removed by the migration and reported (DM-01M497F0NAQARAK3JZFVWF1SD0). No minimal list equals a default list for the same key, so reset and report classes never collide. | same |
| R3 | Normalizer [] lists | m_3_2_x_normalize_activation_absence wrote [] (nothing active) for absent keys from 3.2.6rc1, contradicting the live absent-means-all contract (pack_context.py:738, ArtifactKind.effective_when_absent). The migration is neutralised and the cutover resets [] per-artifact keys to absent with a report (DM-01M497EW60HNWWJQCXDFA99R0H). | research/runtime-seams.md §1.5 |
| R4 | Run-first ordering | runs_first flag on BaseMigration, applied in MigrationRegistry.get_applicable; get_all() keeps version order (chain-integrity test). detect() is content-based. Rejected: sentinel low target version (never selected on newer projects), runner pre-phase (preview ≠ run). | runtime-seams §1.3 |
| R5 | Older migrations | rc35 → stub (detect() false, recorded skipped; precedent m_2_1_2_fix_charter_doctrine_skill); normalizer → stub; m_2_1_2_fix_glossary_context_skill → stub; module-level imports of deleted modules removed (rc35, m_unify_charter_activation); m_3_1_1_charter_rename stops recreating spec-kitty-charter-doctrine; finalize drops the compat-helper call. | runtime-seams §1.4–1.5 |
| R6 | Worktrees | Upgrade skips lane/coord/mission worktrees (#5457, runner.py:81-112); .kittify/-only lane commits are exempt from approved_bound (reconciliation.py:1277), .gitignore/agent-dir changes are not. Runbook: merge the upgraded target into a lane, never rebase (APPROVAL_STAMP_NOT_ON_LANE). | runtime-seams §2 |
| R7 | Detection seam | One predicate next to check_schema_version in _run_startup_project_gates (specify_cli/__init__.py:165), shared with the migration's detect(); two stats plus a prefiltered config.yaml read (never charter.yaml, 155 KB here); exempt upgrade, init, --version, --help; checks the current checkout too (stale lane). load_governance_config fails closed on a legacy key (unknown keys are otherwise ignored → silent loss). | runtime-seams §3 |
| R8 | Doctrine group | 11 leaves; fetch/new/validate/org already share handlers; callers in packs.yml:225,229, builtin_manifest.py:48 (generated_by, regenerate), AGENTS.md (CLAUDE.md symlink), 9 files in packs/internal, completion manifest, ci-router.yml:233. doctor doctrine → doctor charter-packs, JSON keys kept. | runtime-seams §4 |
| R9 | Skill retirement | RETIRED_CANONICAL_SKILL_NAMES retires user-global copies on every run (agent_skills.py:196-206); project roots via the upgrade finalizer's manifest; unmanifested copies removed by a hash-matched step in the cutover migration, edited copies kept and reported. Agent copies are git-ignored here; regenerate completion manifest, docs/api/skills/*, regen fixtures, pack manifest. | runtime-seams §5 |
| R10 | Error codes | CHARTER_PACK_CONFIG_INVALID hard-coded once (charter/mission_type.py:143), golden mission_create_refusals.json:601; not in ERROR_CODES.md (FR-009 adds it under the new name). | runtime-seams §6 |
| R11 | Package split | Ruled destinations work after prep moves: hash helpers and org_extends to offering, absorb_synthesis_manifest to activation, OrgCharterPolicy steps behind hooks, pure manifest writer out of snapshot; new charter.packs facade; charter.drg exports CORE_KIND_PLURALS, resolve_relative_path_within_root; _layer_roots → charter.activation.layer_roots (no blocker); config.py folded into org_charter. 7-step order, leaves first, atomic delete last. | research/package-split-and-paths.md §A |
| R12 | Project pack paths | kernel/charter_pack_paths.py replaces kernel/doctrine_root.py; layer_roots["project"] returns the pack root. 56 path sites in 50 files (12 write); state contract project_doctrine_graph; .gitignore:97-114. Gate modelled on test_charter_path_literal_authority.py plus f-string and alias-constant detection, per-shape planted tests. Staging subtree and org nested layout follow the flat project root; frozen migrations exempt by file. | package-split-and-paths §B |
| R13 | Rename sizing | src: 1,087 "doctrine" identifier tokens in 139 files (DoctrineService 181 lines in 42 files); tests: 2,618 tokens in 471 files; tests/doctrine/ 193 files. Split R1 offering/facades/kernel (~21), R2 activation (~42), R3 specify_cli surface (~45), R4 migrations/synthesizer/gates (~15); tests/doctrine/ directory last. | package-split-and-paths §C |
| R14 | Post-spec squad | Architecture, testability and consumer findings folded into spec.md revision 1d82b937. | research/postspec-squad-*.md |
No dependency is added, upgraded or removed, so the supply-chain controls (DIRECTIVE_051) do not apply.