Research: Universal Charter Rename
Feature: 063-universal-charter-rename Date: 2026-04-04 (revised)
Research Question 1: Can migration files be safely renamed?
Decision
Yes. Migration files can be renamed without breaking the migration system.
Rationale
The migration system discovers files by m_*.py glob pattern via pkgutil.iter_modules(). Registration uses the @MigrationRegistry.register decorator which stores migrations by their migration_id class attribute, not filename. Applied-migration tracking in .kittify/metadata.yaml also uses migration_id strings.
Evidence
src/specify_cli/upgrade/migrations/__init__.py:auto_discover_migrations()scans form_*.pyfilessrc/specify_cli/upgrade/registry.pyline 42:migration_id = migration_class.migration_idsrc/specify_cli/upgrade/runner.pyline 178:metadata.has_migration(migration.migration_id)src/specify_cli/upgrade/metadata.pyline 152: checksm.id == migration_id
Research Question 2: Can old migrations be safely converted to stubs?
Decision
Yes. Old constitution-related migrations can be converted to no-op stubs if a comprehensive charter-rename migration subsumes all their functionality.
Rationale
The migration runner checks migrations in this order: 1. metadata.has_migration(id) → already applied? Skip. 2. migration.detect(project_path) → old state present? If False, record "skipped". 3. migration.can_apply(project_path) → safe to apply? 4. migration.apply(project_path) → transform state.
For previously-upgraded users: Step 1 catches old migrations (metadata says "success") → skip. Charter-rename detects Layout A → migrates.
For new users upgrading from old versions: Stubs return detect() → False → recorded as "skipped". Charter-rename runs and handles ALL layouts (A, B, C) comprehensively.
For fresh installs: All migrations return detect() → False → all skipped. No old state exists.
Critical prerequisite
The charter-rename migration must handle every layout that the old migrations handled:
- Layout C (pre-0.10.12):
.kittify/missions/*/constitution/— what m_0_10_12 handled - Layout B (pre-2.0):
.kittify/memory/constitution.md— what m_2_0_0 handled - Layout A (post-2.0):
.kittify/constitution/— the normalized form - Bootstrap blocks in agent prompts — what m_2_0_2 inserted
- Skill file updates — what m_2_1_2 handled
- m_0_13_0 was already a permanent no-op (superseded by canonical context architecture)
Evidence
m_0_13_0detect()already returnsFalsewith message "Command templates were removed in WP10"- Runner records "skipped" for migrations where
detect()returns False (runner.py line 188-202) - Old users with metadata records are caught at step 1 before detect/apply ever runs
Research Question 3: How to bridge old migration IDs to new ones?
Decision
Add a _LEGACY_MIGRATION_ID_MAP dict in metadata.py that normalizes old constitution-era IDs to charter-era IDs at metadata load time.
Rationale
When ProjectMetadata.load() reads .kittify/metadata.yaml, it checks each applied migration record against the map. Old IDs are silently rewritten to new IDs and persisted. This runs BEFORE the migration loop, so by the time the runner checks has_migration("0.10.12_charter_cleanup"), the metadata already contains the new ID.
Design
_LEGACY_MIGRATION_ID_MAP = {
"0.10.12_constitution_cleanup": "0.10.12_charter_cleanup",
"0.13.0_update_constitution_templates": "0.13.0_update_charter_templates",
"2.0.0_constitution_directory": "2.0.0_charter_directory",
"2.0.2_constitution_context_bootstrap": "2.0.2_charter_context_bootstrap",
"2.1.2_fix_constitution_doctrine_skill": "2.1.2_fix_charter_doctrine_skill",
}
On load: iterate applied_migrations, replace any record.id found in map keys with the map value, persist if changed.
Implication
The 5 dictionary keys contain "constitution" strings. These are the ONLY occurrence of "constitution" in metadata.py — they exist solely as backward-compatibility lookup keys. This is one of the 2 precisely bounded exceptions defined in the spec (NFR-001).
Alternatives Considered
- Keep old migration_ids unchanged: Leaves "constitution" in all 5 migration files' class attributes AND in user metadata forever — rejected
- Change IDs without normalization: Runner can't match old metadata → re-runs old migrations — rejected
- Pre-migration hook: Extra complexity for the same result — rejected
Research Question 4: What generated content embeds "constitution"?
Decision
Generated files under .kittify/constitution/ embed "constitution" in multiple ways. The migration must rewrite all of them.
Evidence from compiler.py, sync.py, context.py:
charter.md (written by _render_constitution_markdown()):
- Header:
# Project Constitution - Comment:
<!-- Generated by 'spec-kitty constitution generate' -->
references.yaml (written by _write_references_yaml()):
source_path: ".kittify/constitution/interview/answers.yaml"source_path: ".kittify/constitution/..."in reference entries
context-state.json (written by build_constitution_context()):
- Schema tracks first-load timestamps per action, may embed path context
Agent prompt files (written by m_2_0_2 bootstrap):
spec-kitty constitution context --action {action} --jsoncommand strings## Governance Contextblocks containing the old command
Migration action
After path moves, perform case-insensitive find-and-replace of "constitution" → "charter" in all text files under .kittify/charter/. Also find-and-replace the bootstrap command in all deployed agent prompt files.
Research Question 5: What worktree/runtime code references constitution?
Decision
Three source files manage worktree constitution state and must be updated.
Evidence
src/specify_cli/core/worktree.py setup_feature_directory() (lines 440-494):
- Creates symlinks from worktree
.kittify/memory→ main repo.kittify/memory - Comment: "Setup shared constitution and AGENTS.md via symlink"
- After the rename, symlink targets change from
memory(which containedconstitution.md) to includecharter/
src/specify_cli/template/manager.py copy_constitution_templates() (lines 42-68):
- Copies toolguide templates into
.kittify/memory/templates/ - Function name and any internal references must be renamed
src/specify_cli/cli/commands/init.py:
- Constitution setup during project initialization
- Must scaffold
.kittify/charter/instead of.kittify/constitution/
tests/git_ops/test_worktree.py (3 matches):
- Lines 339, 349, 875: Create and assert
constitution.mdin worktree memory - Must be updated to
charter.mdin charter paths
Research Question 6: What doctrine mission artifacts embed "constitution"?
Decision
9 files across 3 mission types contain "constitution" references. These are shipped prompt/template text, not code.
Evidence
src/doctrine/missions/software-dev/mission.yaml: 1 matchsrc/doctrine/missions/software-dev/templates/plan-template.md: 2 matches ("Constitution Check" section)src/doctrine/missions/software-dev/templates/task-prompt-template.md: 1 match (.kittify/constitution/constitution.mdpath)src/doctrine/missions/software-dev/actions/specify/guidelines.md: 1 matchsrc/doctrine/missions/software-dev/actions/plan/guidelines.md: 5 matches ("Constitution Compliance" section)src/doctrine/missions/software-dev/actions/implement/guidelines.md: 1 matchsrc/doctrine/missions/software-dev/actions/review/guidelines.md: 1 matchsrc/doctrine/missions/documentation/templates/plan-template.md: 1 matchsrc/doctrine/missions/research/templates/task-prompt-template.md: 1 match
All are text content replacements (no structural changes needed).
Research Question 7: What is the full documentation surface?
Decision
The documentation surface is significantly larger than initially inventoried: 100+ matches across 30+ files.
Key areas missed in original inventory:
README.md: 9 matches (slash command references, path descriptions, workflow examples)docs/reference/cli-commands.md: 27 matches (full constitution command group documentation)docs/reference/slash-commands.md: 18 matches (slash command docs)docs/reference/file-structure.md: 3 matchesdocs/reference/configuration.md: 8 matchesglossary/contexts/governance.md: 16 matches (term definitions)glossary/contexts/configuration-project-structure.md: 6 matchesglossary/contexts/doctrine.md: 13 matchessrc/kernel/README.md: 5 matches.kittify/AGENTS.md+overrides/AGENTS.md: 6 matches total.kittify/memory/058-architectural-review.md: 7 matches