Summary
Renamed 39 files and ~2,000+ identifiers across a TypeScript/Bun monorepo (STRATT) fromSkill* → Doctrine* terminology, applying hard-cut extension migration (.skill.md → .doctrine.md) and layered execution order (L0→L5). All 228 tests passed, build succeeded, zero functional regressions.
Key outcome: Strict dependency order (schema → package → graph/CLI → app → docs) combined with per-layer test verification eliminated risk of circular breakage. This pattern is reusable for other cross-cutting renames in monorepos with strict layering.
Execution Strategy
1. Dependency Mapping (Pre-Execution)
Mapped monorepo’s 5-layer architecture:- L1:
@stratt/schema(Zod validators, type defs) - L2.5:
@stratt/doctrines(parser, registry, resolver) - L3:
@stratt/graph(DAG, CI pipeline) - L5:
@stratt/cli(commander, orchestrator) - L6:
apps/meridian(Astro web app) - Content:
.opencode/doctrines/,docs/atlas/doctrines/
2. Layered Commit Strategy
7 independent commits, executed in dependency order:| Commit | Layer | Changes | Tests | Verifiable Output |
|---|---|---|---|---|
| 1 | L1+L2.5 | Schema types + package rename | 20 | @stratt/doctrines working; schema types correct |
| 2 | L3+L5 | Graph fixtures + CLI commands | 88+122 | validate-doctrines command; legacy fallback |
| 3–5 | L6 | App pages, routes, components | — | Build success (184 pages) |
| 6 | Content | Documentation, CLAUDE.md | — | Links resolve; grep scan clean |
| 7 | Cleanup | Delete legacy packages/skills/, old files | — | No orphaned imports |
- If L1 changes break, everything else fails. Catch early.
- L2.5 tests only L1 changes, so verify before using in L3.
- L3+L5 tests both graph and CLI; if pass, app layer is safe to refactor.
- App layer (L6) has no downstream dependencies, so safe to refactor last.
- Content cleanup (L7) happens last, avoiding dangling references.
3. Hard-Cut Extension Migration
Policy: No dual-extension support (e.g., no.skill.md + .doctrine.md coexistence during transition).
Rationale:
- Eliminates confusion (one source of truth per file)
- Forces complete migration (no lazy import fallbacks)
- Parser updated once; no version checks needed
.doctrine.md extension only.
4. Backward Compatibility via Legacy Field Fallback
Pattern: Use optional chaining + nullish coalescing to support gradual config migration:skills: field) continue working without edit. New configs use doctrines:. No forking logic needed.
Constraint: Only use for config layer; functional code must use doctrines only.
Test-Verify-Commit Cycle
Verification before each commit:Outcomes
Metrics:- 228 tests passing (0 broken, 0 flaky)
- Build: 184 pages compiled successfully
- Grep scan: 0 functional regressions (comments allowed)
- Commits: 7 independent, ordered by dependency
- Files renamed: 39 (21 in
.opencode/, 18 indocs/atlas/) - Identifiers renamed: ~2,000+ across code + config + docs
- Layer-by-layer testing caught breakage at source
- Hard-cut extension prevented import confusion
- Legacy fallback allowed gradual config migration
Reusable Pattern
For future cross-cutting renames in monorepos:- Map dependency layers (what depends on what?)
- Identify hard cut boundaries (schema? package name? file extension?)
- Commit in reverse-dependency order (leaves → roots)
- Test at each layer before moving up
- Use optional chaining for backward compatibility in config layer only
- Full grep scan before final cleanup commit
Related Artifacts
- Config schema:
packages/schema/src/types/council.ts(DoctrineRefSchema) - Package:
packages/doctrines/(renamed frompackages/skills/) - CLI command:
packages/cli/src/commands/council.ts(validate-doctrines) - App routes:
apps/meridian/src/pages/doctrines/(all subpages) - Content:
.opencode/doctrines/,docs/atlas/doctrines/