Summary
Migrated 39 YAML documentation files from.skill.md → .doctrine.md extension using a hard-cut strategy (no dual-extension coexistence period). Combined with synchronized parser updates, this eliminated confusion and forced complete migration in a single commit. Contrast with typical “soft migration” approach (supporting both extensions during transition).
Key finding: Hard-cut migrations cost slightly more upfront (1-2 hours) but save months of downstream confusion and technical debt. Dual support requires version checks, import fallbacks, and deprecation warnings—expensive across large teams.
Hard-Cut vs. Soft Migration Trade-offs
Soft Migration (Traditional)
Pattern: Support both extensions during transition period.- Version/time-based deprecation warnings (“Will remove in Q3”)
- Import confusion (“Which one should I use?”)
- Maintenance burden (2 codepaths in parser)
- Delayed team adoption (people keep using old names)
- Technical debt accumulation (~6–12 months before full cleanup)
Hard-Cut Migration (This Work)
Pattern: Rename all at once; no fallback in file layer.- 1–2 hours to coordinate rename + update parser
- All files must rename in single commit (or parallel PRs)
- Config layer still uses legacy fallback for gradual adoption
- Single source of truth (no dual-support logic)
- Parser is simpler (no version checks)
- Team adopts immediately (no choice)
- Technical debt eliminated day 1
Execution Details
1. File Rename (Mass Parallel)
Strategy: Usefind + mv in parallel across directory trees.
.skill.md remain.
2. Parser Update (Synchronous)
Update parser in the same commit as file renames. No intermediate state where parser reads old extension. Before:- No window where old files can’t be read
- No import errors during transition
- Tests verify extension immediately
3. Backward Compatibility (Config Layer Only)
Pattern: Use fallback in config parsing, not file I/O.- Council YAML files aren’t renamed (separate concern)
- Old configs continue working without edit
- New configs use
doctrines:field - Team adopts gradually at their pace
- No urgency to update all council.yaml files simultaneously
4. Test Verification
Unit tests verify extension:Metrics from STRATT Migration
| Metric | Value |
|---|---|
| Files renamed | 39 (.skill.md → .doctrine.md) |
| Directories | 2 (.opencode, docs/atlas) |
| Parser lines changed | 4 (glob pattern updated) |
| Config fallback added | 1 (nullish coalescing) |
| Tests updated | 0 (tests were generic, worked without update) |
| Commits | 1 (synchronized rename + parser update) |
| Downstream impact | 0 (no broken imports) |
| Time to execute | ~45 minutes |
Why Hard-Cut Works
- Clear signal to team: Old name is gone; no workarounds available
- Parser simplicity: One codepath (no version checks or deprecation warnings)
- Test enforcement: CI prevents regression to old pattern
- Config fallback: Gradual adoption of new field names without code pressure
When to Use Hard-Cut
✅ Use hard-cut if:- File format/extension is internal (not public API)
- Team is coordinated (can merge + deploy simultaneously)
- Tests cover the extension (parser tests verify .doctrine.md)
- Alternative fallbacks exist for gradual adoption (e.g., config layer)
- Files are public or user-facing (breaking change)
- Multiple teams/repos depend on old format (need transition period)
- No tests cover extension (can’t enforce immediately)
Related Artifacts
- Files renamed: 39 across
.opencode/doctrines/anddocs/atlas/doctrines/ - Parser:
packages/doctrines/src/parser.ts - Config fallback:
packages/cli/src/lib/council.ts(line ~42) - Commit: STRATT Phase 1, Commit 1 (“Schema + Doctrines Package”)
- Test fixtures:
packages/doctrines/tests/fixtures.ts(uses .doctrine.md only)
Reusable Checklist for Hard-Cut Migrations
- Identify all file locations (glob pattern)
- Write tests that explicitly check new extension
- Plan parser/loader updates (synchronous with renames)
- Identify config layers that need fallbacks (separate from functional code)
- Execute mass rename (find + mv in parallel)
- Verify:
find . -name "*.OLD.md" | wc -l→ 0 - Update parser/loader in same commit
- Run full test suite (should pass immediately)
- Grep scan to confirm new extension is used
- Commit with clear message (“Hard-cut: .skill.md → .doctrine.md”)