Decision: Version Numbering & Approval Horizon
Status: ✅ Approved & fully implemented; holding steady in production Date: 2026-04-19 Last update: 2026-04-20 (post-migration stability review)
Rule
Versions
All versions are 3-part semver. No v prefix. Storage and display match exactly.
0.14.1 0.15.0 1.0.0 2.0.0
Sort by semver. That's it.
Approval horizon
One field per project: project.autonomy.approvedThrough (a semver string).
- Agent executes any task whose version ≤
approvedThrough. - Agent does not execute anything above it, ever.
- Only a human moves
approvedThrough. - Auto-advance within horizon, hard-stop at horizon. When all tasks in the current version ship, the system automatically promotes the next planned version to
currentand moves its planning tasks into backlog — but ONLY if that next version is still ≤ horizon. If it exceeds horizon, the system stops and waits for a human to bumpapprovedThrough. - In-progress & QA tasks are grandfathered — once approved to start, they finish even if the horizon doesn't move.
No states, no tags, no notes, no audit log, no patch/minor distinction at the rule level. Humans assign version numbers; semver handles order.
Why this works
- One mental model, one field, one comparison.
- No surprise execution — human always extends the leash explicitly.
- Nothing to maintain: no state machine, no lifecycle enum, no backfill scripts when rules change.
- Trust the human to pick good version numbers and sensible horizon moves.
Implementation status — ✅ Complete
| # | Item | Status |
|---|------|--------|
| 1 | All DB versions in 3-part semver (112 roadmap rows + project currentVersion) | ✅ |
| 2 | All parseFloat on versions removed from src/ (10 files) | ✅ |
| 3 | project.autonomy.approvedThrough is the single source of truth | ✅ |
| 4 | Version completion no longer bumps approvedThrough (auto-advance killed) | ✅ |
| 5 | Backlog-pull horizon-gated (scheduler.ts + scheduler/route.ts) | ✅ |
| 6 | In-progress + QA tasks grandfathered | ✅ |
| 7 | Vision auto-approve flow quarantined (complete, callback, approve, reject → 410 Gone) | ✅ |
| 8 | roadmap-sync.ts simplified — only marks current version shipped, no auto-launch | ✅ |
| 9 | Dead autoAdvance flag removed from UI (3 sites) + store integrity guard + DB (9 projects) | ✅ |
| 10 | v prefix stripped from ~25 display + log + template sites | ✅ |
| 11 | Dead top-level project.approvedThrough column dropped from DB (14 projects) | ✅ |
| 12 | Legacy parseFloat-on-version scripts deleted (reconcile-roadmap-items.mjs, repair-current-version.mjs) | ✅ |
| 13 | API validation: POST /api/roadmap/[projectId] rejects non-semver (no v, no 2-part) | ✅ |
| 14 | ORG.md generator includes "Versions & Approval Horizon" section so all agents follow the rule | ✅ |
Backward compat preserved
vision-completion.tsregex accepts both### v0.14(legacy) and### 0.14.0(new) headers when reading existing VISION.md files. New headers are emitted withoutv.- DB read paths defensive:
autonomy?.approvedThrough || nulleverywhere. Missing field = no execution (safe default).
Audit history
- 2026-04-19 (initial migration): parseFloat → semver in
projects/[id]/page.tsx+ DB version normalization. Missed 9 other files using parseFloat on versions, missed roadmap-versions table, missed top-level vs nestedapprovedThroughfield mismatch. - 2026-04-19 (part 2): swept remaining parseFloat sites (10 files), migrated roadmap-versions table (112 rows) + project approvedThrough field.
- 2026-04-19 (KISS pass): consolidated
approvedThroughto nested location, killed auto-advance, added horizon-gated task filter in scheduler. - 2026-04-19 (cleanup pass): items 7-14 above. Quarantined parallel auto-approve flow, removed dead
autoAdvanceflag, strippedvprefix everywhere, added API validation, taught ORG.md generator the rule, dropped dead DB fields, deleted 2 legacy scripts. - 2026-04-20 (auto-advance within horizon): Added guarded auto-advance in
roadmap-sync.ts. When a version's tasks all ship: mark shipped → find next planned → if ≤ horizon, promote to current + move tasks to backlog; if > horizon, hard stop. Horizon itself is never written by this code path. Closes "click Launch once per version" UX gap while preserving human-holds-leash contract. - 2026-04-20 (stability review): Convention has survived three sprints (v0.14 cleanup, v0.14.1 composer, v0.15 polish). No parseFloat regressions. No
v-prefix regressions.invalid_versionAPI guard has rejected zero malformed writes in the wild (regex held). Auto-advance fired cleanly for 0.14 → 0.14.1. Open observation:currentVersiononproj-mcdrifted tonullat some point during the v0.15 sprint — likely a race between roadmap-sync and a manual edit. Symptom: auto-advance couldn't engage even though horizon was 0.16. Remediation is operational (PATCHcurrentVersion on the project), not structural — rule still stands.
Verified contract (smoke tests passed 2026-04-19 23:15 EDT)
# Reject `v` prefix
$ curl -X POST /api/roadmap/proj-mc -d '{"action":"upsert","version":"v0.99",...}'
{"error":"invalid_version","message":"Version \"v0.99\" is not valid semver..."}
# Reject 2-part
$ curl -X POST /api/roadmap/proj-mc -d '{"action":"upsert","version":"0.99",...}'
{"error":"invalid_version","message":"Version \"0.99\" is not valid semver..."}
# ORG.md teaches the rule
$ curl /api/org-context?agent=mikey | grep "Versions & Approval Horizon"
## Versions & Approval Horizon
- **Versions are 3-part semver.** ...