feat(P06): 3-tier versioning, branch hierarchy enforcement, ARCHITECTURE-PLAN synthesis

---ci---
phase: 6
milestone: v0.5
status: complete
decisions:
  - id: D-006
    decision: Research as intermediate work product
    rationale: Conclusions update .ci/ files; full research doc intentionally not preserved
    confidence: 0.90
  - id: D-007
    decision: Branch hierarchy enforcement: main > milestone > phase
    rationale: Prevents out-of-order merges and semantically wrong tags
    confidence: 0.92
  - id: D-008
    decision: 3-tier versioning: NFR/feature/schema-breaking
    rationale: Patch per phase (NFR/feature) or minor per phase (schema-breaking); milestone gets minor (feature) or major (schema-breaking)
    confidence: 0.95
requirements:
  covered: [VER-06, BRANCH-01, BRANCH-02, ARCH-01]
---/ci---

- Synthesize ARCHITECTURE-PLAN.md into .ci/ci/ARCHITECTURE.md (expanded 51→230 lines)
- Add D-006/D-007/D-008 to .ci/ci/PROJECT.md key decisions table
- Delete ARCHITECTURE-PLAN.md after synthesis
- Rewrite ship.md with 3-tier versioning model + branch hierarchy merge flows
- Rewrite branch-strategy.md with 3-tier versioning + branch hierarchy + version validation
- Add MilestoneType to config types
- Replace isNfrMilestone() with getMilestoneType() returning nfr|feature|schema-breaking
- Add validateMergeOrder(), mergeMilestoneBranch(), computeMilestoneTag() to GitBranch
- Add computeShipVersion(), validateVersionOrder(), resolveMergeTarget() to ship command
- Remove hardcoded v0.5. from error-recovery rollback
- Create .githooks/pre-push for semver ordering + branch hierarchy validation
- Add 15 new tests (370 total, all passing)
This commit is contained in:
Jon Chery
2026-05-29 17:18:10 +00:00
parent 3d069319b5
commit ab6af144b7
14 changed files with 696 additions and 93 deletions
+19 -6
View File
@@ -2,6 +2,7 @@ import * as fs from "node:fs";
import * as path from "node:path";
import { writeFile, readFile, ensureDir, fileExists } from "../utils/file.js";
import { PipelineStage } from "../types/pipeline.js";
import { MilestoneType } from "../types/config.js";
const CI_DIR = ".ci";
@@ -467,19 +468,31 @@ export class CiFiles {
this.writeRoadmapMd(roadmap);
}
isNfrMilestone(): boolean {
getMilestoneType(): MilestoneType {
const roadmap = this.readRoadmapMd();
if (!roadmap) return true;
if (!roadmap) return "nfr";
const nfrTypes: string[] = ["fix", "chore", "docs", "perf", "refactor", "test"];
const schemaBreakKeywords: string[] = ["refactor", "rewrite", "rearchitecture", "migrate", "restructure"];
let hasFeature = false;
let hasSchemaBreak = false;
for (const phase of roadmap.phases) {
if (phase.status === "in_progress" || phase.status === "not_started") {
if (phase.status === "in_progress" || phase.status === "not_started" || phase.status === "complete") {
const phaseName = phase.name.toLowerCase();
const hasFeature = !nfrTypes.some((t) => phaseName.includes(t)) && !phaseName.includes("bug") && !phaseName.includes("tune") && !phaseName.includes("refresh");
if (hasFeature) return false;
const isNfr = nfrTypes.some((t) => phaseName.includes(t)) || phaseName.includes("bug") || phaseName.includes("tune") || phaseName.includes("refresh");
if (!isNfr) hasFeature = true;
if (schemaBreakKeywords.some((k) => phaseName.includes(k))) hasSchemaBreak = true;
}
}
return true;
if (hasSchemaBreak) return "schema-breaking";
if (hasFeature) return "feature";
return "nfr";
}
isNfrMilestone(): boolean {
return this.getMilestoneType() === "nfr";
}
private parseProjectMd(content: string): ProjectMd {