Summary
PR #245 added discrete-mode coercion in `gate.ts` so every stage gate forces external PR/MR review (`review: auto` → `external`, `review: ask` → `ask,external` compound, etc.). This issue tracks the hardening followups that didn't land in #245.
What's already in place (verified during #245)
- `ensureOnStageBranch()` runs at the top of `haiku_run_next` (`tools/orchestrator/haiku_run_next.ts:176`) — pickup-from-main reroutes to the active stage branch, forward-merges main → stage if drifted.
- Merge-detection gates completion — when `status: completed + gate_outcome: blocked`, the gate handler at `gate.ts:449` calls `isBranchMerged(stageBranch, mainline)` and only flips to `gate_outcome: advanced` after the actual merge happens. Local approve alone never advances the stage.
What's still missing
1. `writeOnIntentMain()` for intent-scope frontmatter writes
`intent.md` is intent-scope metadata. Today it's written via `setFrontmatterField(intentFile, ...)` from ~25 call sites, including some that run while checked out on a stage branch. Result: `intent.md` can diverge between the stage branch and intent main (observed bug: `twelve-week-plan-accountability-app` had `active_stage: product` on intent main while the stage branch had `active_stage: development`).
Fix shape:
- Introduce a `setIntentField()` helper that wraps `setFrontmatterField` AND mirrors the write to intent main via `writeOnIntentMain()`.
- Migrate every intent-scope FM call site (`active_stage`, `status`, `archived`, `phase`, `started_at`, `completed_at`, `completion_review_*`, `autopilot`, `mode`).
- Add a startup/pre-tick consistency check that compares `intent.md`'s key fields between the active stage branch and intent main; surface as a guard error if they diverge.
2. Pre-tick `active_stage` repair (✅ shipped in #245 — kept here for cross-reference)
`preTickConsistency` in `workflow/pre-tick.ts` now derives `active_stage` from state.json walk and rewrites `intent.md` when stale. This masks the divergence bug above as a self-healing safety net, but the underlying writes-on-stage-branch is what should actually be fixed.
3. Mode-change tool (out-of-scope for this issue, mentioned for completeness)
There's currently no `haiku_intent_change_mode` tool. Mode is fixed at intent creation. If we want to support mode migration (continuous ↔ discrete), it needs its own dedicated tool with branch-migration logic — not a frontmatter edit.
Acceptance criteria
Summary
PR #245 added discrete-mode coercion in `gate.ts` so every stage gate forces external PR/MR review (`review: auto` → `external`, `review: ask` → `ask,external` compound, etc.). This issue tracks the hardening followups that didn't land in #245.
What's already in place (verified during #245)
What's still missing
1. `writeOnIntentMain()` for intent-scope frontmatter writes
`intent.md` is intent-scope metadata. Today it's written via `setFrontmatterField(intentFile, ...)` from ~25 call sites, including some that run while checked out on a stage branch. Result: `intent.md` can diverge between the stage branch and intent main (observed bug: `twelve-week-plan-accountability-app` had `active_stage: product` on intent main while the stage branch had `active_stage: development`).
Fix shape:
2. Pre-tick `active_stage` repair (✅ shipped in #245 — kept here for cross-reference)
`preTickConsistency` in `workflow/pre-tick.ts` now derives `active_stage` from state.json walk and rewrites `intent.md` when stale. This masks the divergence bug above as a self-healing safety net, but the underlying writes-on-stage-branch is what should actually be fixed.
3. Mode-change tool (out-of-scope for this issue, mentioned for completeness)
There's currently no `haiku_intent_change_mode` tool. Mode is fixed at intent creation. If we want to support mode migration (continuous ↔ discrete), it needs its own dedicated tool with branch-migration logic — not a frontmatter edit.
Acceptance criteria