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:
@@ -23,7 +23,7 @@ Canonical branch naming and lifecycle conventions for CI. Branches encode projec
|
||||
**Lifecycle:**
|
||||
1. Created at phase start by `GitBranch.createPhaseBranch()`
|
||||
2. All task commits for the phase land on this branch
|
||||
3. Merged to main (squash) on phase completion
|
||||
3. Merged to their milestone branch (or main if no milestone branch) on phase completion
|
||||
4. Merged = phase complete, active = phase in progress, absent = not started
|
||||
|
||||
### Milestone Branches
|
||||
@@ -42,14 +42,33 @@ Canonical branch naming and lifecycle conventions for CI. Branches encode projec
|
||||
**Lifecycle:**
|
||||
1. Created at first phase of milestone by `GitBranch.createMilestoneBranch()`
|
||||
2. Spans multiple phases within the same milestone
|
||||
3. Merged to main on milestone completion
|
||||
4. Merged = milestone complete, active = milestone in progress
|
||||
3. All phase branches merge into this branch on completion
|
||||
4. Merged to main on milestone completion
|
||||
5. Merged = milestone complete, active = milestone in progress
|
||||
|
||||
### Hotfix Branches
|
||||
|
||||
**Format:** `hotfix/description`
|
||||
|
||||
Created for urgent fixes outside the normal phase flow. Merged directly to main.
|
||||
Created for urgent fixes outside the normal phase flow. Merged directly to main (exception to hierarchy).
|
||||
|
||||
## Branch Hierarchy (Enforced)
|
||||
|
||||
```text
|
||||
main ─── milestone/vX.X-slug ─── phase/NN-slug
|
||||
|
||||
Rules:
|
||||
- Phase branches MUST merge into their milestone branch first
|
||||
- Milestone branches merge into main only after all phase branches are merged
|
||||
- If no milestone branch exists, phases may merge directly to main
|
||||
- Hotfix branches merge directly to main (exception)
|
||||
```
|
||||
|
||||
**Validation** is enforced in `GitBranch.mergePhaseBranch()` and `createShipCommand()`:
|
||||
- Phase → main: rejected if milestone branch exists for this milestone
|
||||
- Phase → milestone: allowed
|
||||
- Milestone → main: allowed only after all phase branches are merged
|
||||
- Hotfix → main: allowed
|
||||
|
||||
## Branch Status Inference
|
||||
|
||||
@@ -69,67 +88,97 @@ const branches = gitContext.getBranches();
|
||||
|
||||
## Merge Strategy
|
||||
|
||||
Default: **squash merge** into main.
|
||||
Default: **squash merge**.
|
||||
|
||||
Phase branches squash-merge into their milestone branch. Milestone branches squash-merge into main. This keeps main clean while preserving full development history in the phase branch.
|
||||
|
||||
```typescript
|
||||
gitBranch.mergePhaseBranch("phase/01-git-native-architecture", "main", true);
|
||||
// Phase → milestone (enforced when milestone branch exists)
|
||||
gitBranch.mergePhaseBranch("phase/01-git-native-architecture", "milestone/v0.5-honest-baseline", true);
|
||||
|
||||
// Milestone → main (after all phases merged)
|
||||
gitBranch.mergeMilestoneBranch("milestone/v0.5-honest-baseline", "main", true);
|
||||
```
|
||||
|
||||
Squash merge keeps main clean while preserving full development history in the phase branch. Phase branches can be deleted after merge if desired.
|
||||
Phase branches can be deleted after merge if desired.
|
||||
|
||||
## Versioning and Releases
|
||||
|
||||
**Every merge to main creates a release. No exceptions.** Versioning maps to project structure:
|
||||
**Every merge to main creates a release. No exceptions.** Versioning follows a 3-tier model based on milestone type:
|
||||
|
||||
| Version Part | When | Example |
|
||||
|-------------|------|---------|
|
||||
| **Major** (X.0.0) | Project-level refactor or schema change | `v1.0.0` |
|
||||
| **Minor** (0.X.0) | Every milestone completion | `v0.3.0` |
|
||||
| **Patch** (0.0.X) | Every phase completion | `v0.2.3` |
|
||||
### 3-Tier Versioning Model
|
||||
|
||||
### Phase completion (patch release)
|
||||
| Milestone Type | Condition | Phase release | Milestone release |
|
||||
|---------------|-----------|---------------|-------------------|
|
||||
| **NFR** | All phases: fix/chore/docs/perf/refactor/test | Patch (`vX.Y.Z`) | None |
|
||||
| **Feature** | Any phase is `feat`, no schema break | Patch (`vX.Y.Z`) | Minor — `vX.(Y+1).0` |
|
||||
| **Schema-breaking** | Refactor/schema break/new direction | Minor — `vX.(Y+N).0` per phase | Major — `v(X+1).0.0` |
|
||||
|
||||
**IMPORTANT:** Milestone tags are always the NEXT version, never the base:
|
||||
- Feature: patches v0.5.1–v0.5.5 → milestone tag is v0.6.0 (NOT v0.5.0)
|
||||
- Schema-breaking: minors v0.3.0, v0.4.0, v0.5.0 → milestone tag is v1.0.0
|
||||
- NFR: no milestone tag — the milestone is implicit from the patch sequence
|
||||
|
||||
Determine milestone type via `getMilestoneType()` which returns `"nfr" | "feature" | "schema-breaking"`.
|
||||
|
||||
### Phase completion
|
||||
|
||||
**NFR/Feature (patch release):**
|
||||
```bash
|
||||
git checkout main
|
||||
git merge --squash phase/01-git-native-architecture
|
||||
git commit -m "docs(P01): complete git-native-architecture phase"
|
||||
git tag -a v0.2.1 -m "v0.2.1: git-native-architecture"
|
||||
git checkout milestone/v0.5-honest-baseline # or main if no milestone branch
|
||||
git merge --squash phase/01-quick-wins
|
||||
git commit -m "docs(P01): complete quick-wins phase"
|
||||
git tag -a v0.5.1 -m "v0.5.1: quick-wins"
|
||||
git push origin main --tags
|
||||
# Create Gitea release for v0.2.1
|
||||
# Create Gitea release for v0.5.1
|
||||
```
|
||||
|
||||
Phase number within the milestone determines the patch version (1st phase = .1, 2nd phase = .2, etc.)
|
||||
|
||||
### Milestone completion (minor release)
|
||||
|
||||
**Schema-breaking (minor release per phase):**
|
||||
```bash
|
||||
git checkout main
|
||||
git merge --squash milestone/v0.2-git-native
|
||||
git commit -m "docs(milestone): complete git-native"
|
||||
git tag -a v0.2.0 -m "v0.2.0: git-native"
|
||||
git checkout milestone/v0.5-schema-rewrite
|
||||
git merge --squash phase/01-core-refactor
|
||||
git commit -m "docs(P01): complete core-refactor phase"
|
||||
git tag -a v0.5.0 -m "v0.5.0: core-refactor"
|
||||
git push origin main --tags
|
||||
# Create Gitea release for v0.2.0 with full milestone summary
|
||||
# Create Gitea release for v0.5.0
|
||||
```
|
||||
|
||||
### Major release
|
||||
Each schema-breaking phase bumps the minor. 1st phase = next available minor, 2nd = minor+1, etc.
|
||||
|
||||
When the project undergoes a schema-breaking change (e.g., switching from file-based to git-native architecture), bump the major version. Major releases follow the same merge → tag → release flow.
|
||||
### Milestone completion
|
||||
|
||||
## NFR Milestone Versioning
|
||||
**Feature (minor release):**
|
||||
```bash
|
||||
# All phases already merged into milestone branch
|
||||
git checkout main
|
||||
git merge --squash milestone/v0.5-honest-baseline
|
||||
git commit -m "docs(milestone): complete honest-baseline"
|
||||
git tag -a v0.6.0 -m "v0.6.0: honest-baseline" # NEXT minor, NOT v0.5.0
|
||||
git push origin main --tags
|
||||
# Create Gitea release for v0.6.0 with full milestone summary
|
||||
```
|
||||
|
||||
NFR milestones and feature milestones follow different versioning rules:
|
||||
**Schema-breaking (major release):**
|
||||
```bash
|
||||
# All phases already merged into milestone branch
|
||||
git checkout main
|
||||
git merge --squash milestone/v0.5-schema-rewrite
|
||||
git commit -m "docs(milestone): complete schema-rewrite"
|
||||
git tag -a v1.0.0 -m "v1.0.0: schema-rewrite" # NEXT major
|
||||
git push origin main --tags
|
||||
# Create Gitea release for v1.0.0 with full milestone summary
|
||||
```
|
||||
|
||||
**NFR milestones** — all phases are `fix`, `chore`, `docs`, `perf`, `refactor`, or `test`:
|
||||
- Each phase gets a progressive patch version (v0.1.1, v0.1.2, v0.1.3)
|
||||
- No separate milestone tag — the milestone is implicit from the patch sequence
|
||||
- Example: milestone v0.1 with phases P01 (chore), P02 (test), P03 (perf) → v0.1.1, v0.1.2, v0.1.3
|
||||
**NFR milestones produce no milestone tag.** The last phase's patch version is the final release.
|
||||
|
||||
**Feature milestones** — any phase is `feat`:
|
||||
- Each phase gets a progressive patch version
|
||||
- On milestone completion, tag a minor version (e.g., v0.2.0)
|
||||
- Example: milestone v0.2 with phases P01 (feat), P02 (feat), P03 (fix) → v0.2.1, v0.2.2, v0.2.3 + milestone tag v0.3.0
|
||||
### Version Validation
|
||||
|
||||
Determine milestone type by checking `isNfrMilestone()` which inspects all phase commit types within the milestone.
|
||||
Before creating any tag:
|
||||
1. Tag must be strictly greater than all existing tags on the same major.minor line
|
||||
2. Milestone completion tag must be next minor (feature) or next major (schema-breaking)
|
||||
3. NEVER create a tag that is semantically below existing phase tags
|
||||
|
||||
## Multi-Project Branch Naming
|
||||
|
||||
@@ -155,7 +204,7 @@ const milestones = gitBranch.listMilestones();
|
||||
|
||||
## Branch Creation Rules
|
||||
|
||||
1. Always create phase branches from main (or the current milestone branch)
|
||||
1. Always create phase branches from the current milestone branch (or main if no milestone branch exists)
|
||||
2. Never create a branch for a completed phase — it should already be merged
|
||||
3. Milestone branches span phases — don't create one per phase
|
||||
4. Use `GitBranch.createPhaseBranch()` to ensure consistent naming
|
||||
@@ -164,25 +213,35 @@ const milestones = gitBranch.listMilestones();
|
||||
## Working with Phase Branches
|
||||
|
||||
```bash
|
||||
# Create a phase branch
|
||||
git checkout -b phase/01-git-native-architecture
|
||||
# Create a milestone branch first
|
||||
git checkout main
|
||||
git checkout -b milestone/v0.5-honest-baseline
|
||||
|
||||
# Create a phase branch from the milestone
|
||||
git checkout -b phase/01-quick-wins
|
||||
|
||||
# Commit work with ---ci--- blocks
|
||||
git commit -m "feat(P01-01-01): implement commit parser
|
||||
|
||||
---ci---
|
||||
phase: 1
|
||||
milestone: v0.2
|
||||
milestone: v0.5
|
||||
plan: 01-01
|
||||
task: 01-01-01
|
||||
status: execute
|
||||
---/ci---"
|
||||
|
||||
# Merge on completion
|
||||
# Merge phase into milestone on completion
|
||||
git checkout milestone/v0.5-honest-baseline
|
||||
git merge --squash phase/01-quick-wins
|
||||
git commit -m "docs(P01): complete quick-wins phase"
|
||||
git tag -a v0.5.1 -m "v0.5.1: quick-wins"
|
||||
|
||||
# After all phases, merge milestone into main
|
||||
git checkout main
|
||||
git merge --squash phase/01-git-native-architecture
|
||||
git commit -m "docs(P01): complete git-native-architecture phase"
|
||||
git tag -a v0.2.1 -m "v0.2.1: git-native-architecture"
|
||||
git merge --squash milestone/v0.5-honest-baseline
|
||||
git commit -m "docs(milestone): complete honest-baseline"
|
||||
git tag -a v0.6.0 -m "v0.6.0: honest-baseline"
|
||||
git push origin main --tags
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user