forge-orkes 0.59.1 → 0.62.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forge-orkes",
3
- "version": "0.59.1",
3
+ "version": "0.62.0",
4
4
  "description": "Set up the Forge meta-prompting framework for Claude Code in your project",
5
5
  "bin": {
6
6
  "create-forge": "./bin/create-forge.js"
@@ -24,6 +24,16 @@
24
24
  # SLICE_RESULT phases=<completed> touches=<operator-touches> last_ping=<name>
25
25
  # All human/progress logging goes to stderr.
26
26
  #
27
+ # PARK -> ANSWER -> RESUME (the operator loop). When a phase parks (verdict=park) with an
28
+ # interrupt question, the operator answers by writing an ANSWER FILE at the conventioned path
29
+ # <work-dir>/phase-<N>/answer.md (beside that phase's phase-report.yml; N = phase index)
30
+ # On the next invocation the runner re-assembles THAT phase's prompt with its own parked report
31
+ # + the answer file, so the fresh-context re-run sees what it asked and the answer, and resumes
32
+ # past the ambiguity instead of replaying cold. A fresh park AFTER a new answer is a NEW ping
33
+ # (the park dedup key folds in the answer's cksum fingerprint); a crash-resume with the same
34
+ # answer state re-pings nothing. Files-only + recompute-from-disk still hold (Fork 3 / NFR-028):
35
+ # the answer file is another on-disk input, not runner-resident state.
36
+ #
27
37
  # Pure POSIX sh — no arrays, no bashisms, dependency-free (NFR-026).
28
38
  set -eu
29
39
 
@@ -378,6 +388,26 @@ for phase in $PHASES; do
378
388
  done
379
389
  fi
380
390
 
391
+ # Resume context: if THIS phase already parked on a previous run, show it its OWN parked
392
+ # report + the operator's answer (if written) so the fresh-context re-run resumes past the
393
+ # ambiguity instead of replaying cold (the park->answer->resume loop). Read $report BEFORE
394
+ # the launch below overwrites it — assembly runs first, so $report still holds the prior
395
+ # (parked) report at this point. Answer file convention: $wdir/answer.md (see header).
396
+ if [ -f "$report" ] && [ "$(report_field "$report" verdict)" = "park" ]; then
397
+ printf '## Your previous park on this phase (you asked; the operator may have answered)\n\n'
398
+ printf 'You parked here on a prior run. Your prior phase-report.yml:\n\n'
399
+ sed 's/^/ /' "$report"
400
+ printf '\n'
401
+ if [ -f "$wdir/answer.md" ]; then
402
+ printf "### The operator's answer\n\n"
403
+ cat "$wdir/answer.md"
404
+ printf '\n\nUse this answer to proceed. Do NOT re-park on the same question once it is resolved;\n'
405
+ printf 'park again only if a genuinely NEW ambiguity/decision remains.\n\n'
406
+ else
407
+ printf 'No answer file yet at %s — if you still cannot proceed, park again.\n\n' "$wdir/answer.md"
408
+ fi
409
+ fi
410
+
381
411
  printf '## Repo state (slice worktree)\n\n'
382
412
  printf '```\n'
383
413
  git -C "$WORKTREE" log --oneline -5 2>/dev/null || true
@@ -564,7 +594,14 @@ for phase in $PHASES; do
564
594
 
565
595
  case "$interrupt" in
566
596
  ambiguity|irreversible|fork|budget)
567
- slice_notify "$interrupt" "$phase" "$ipayload"
597
+ # Answer-aware dedup key: fold the answer file's fingerprint into the ping key so a
598
+ # re-park AFTER a new answer fires a FRESH ping (new key), while a crash-resume in the
599
+ # SAME answer state stays suppressed (unchanged key — preserves the crash-dedup intent).
600
+ # `none` when unanswered. cksum is POSIX + dependency-free (NFR-026). Scoped to THIS
601
+ # (phase-self-park) branch only — refresh/divergence/budget/halt/done keys are untouched.
602
+ _ans_fp=none
603
+ [ -f "$wdir/answer.md" ] && _ans_fp="$(cksum "$wdir/answer.md" 2>/dev/null | cut -d' ' -f1)"
604
+ slice_notify "$interrupt" "$phase-a$_ans_fp" "$ipayload"
568
605
  printf '[runner] phase %s: PARKED — §8 %s interrupt (stopping before next phase)\n' "$phase" "$interrupt" >&2
569
606
  printf 'SLICE_RESULT phases=%s touches=%s last_ping=%s\n' "$COMPLETED" "$TOUCHES" "$LAST_PING"
570
607
  exit 3
@@ -142,7 +142,7 @@ Document in `.forge/phases/milestone-{id}/{phase}-{name}/contracts/`.
142
142
 
143
143
  1. **Persist** — Confirm ADRs in `.forge/decisions/`, models and contracts in `.forge/phases/milestone-{id}/{phase}-{name}/`
144
144
  2. **Update state** — Set `current.status` to `planning` in `.forge/state/milestone-{id}.yml`
145
- 3. **State-sync commit** (State Commit Protocol): `git add .forge/` then `git commit -m "chore(forge): sync state after architecting — m{N} {phase-name}"` (scoped; never `git add .`).
145
+ 3. **State-sync commit** (State Commit Protocol): `git -C {worktree_path} add .forge/` then `git -C {worktree_path} commit -m "chore(forge): sync state after architecting — m{N} {phase-name}"` (worktree form — recorded `lifecycle.worktree_path` is authoritative over cwd; see FORGE.md State Commit Protocol. Scoped; never `git add .`).
146
146
  4. **Recommend context clear:**
147
147
 
148
148
  *"Architecture decisions written and synced. `/clear` then `/forge` to continue with planning."*
@@ -338,5 +338,5 @@ If milestone exists → skip to Step B.
338
338
  - Add any unresolved items to `## Needs Resolution`
339
339
  - If `context.md` already exists (post-planning discussion), update relevant sections + log amendments
340
340
  2. **Update state** -- `current.status` = `planning` (`architecting` for Full) in milestone yml
341
- 3. **State-sync commit** (State Commit Protocol): `git add .forge/` then `git commit -m "chore(forge): sync state after discussing — m{N} {name}"` (scoped; never `git add .`).
341
+ 3. **State-sync commit** (State Commit Protocol): `git -C {worktree_path} add .forge/` then `git -C {worktree_path} commit -m "chore(forge): sync state after discussing — m{N} {name}"` (worktree form — recorded `lifecycle.worktree_path` is authoritative over cwd; see FORGE.md State Commit Protocol. Scoped; never `git add .`).
342
342
  4. **Recommend clear:** *"State synced and verified ({N} decisions in context.md). `/clear` then `/forge` for {planning/architecting}."*
@@ -10,10 +10,17 @@ description: "Build to plan with atomic commits, deviation rules, and context en
10
10
  - [ ] Context.md locked decisions noted
11
11
  - [ ] Constitution.md gates satisfied
12
12
  - [ ] Milestone state updated to `status: executing`
13
+ - [ ] Worktree cwd verified — git ops pinned to the recorded path (see below)
13
14
  - [ ] Workspace prerequisites met — submodules populated (see below)
14
15
  - [ ] Baseline snapshot captured (see below)
15
16
  - [ ] Plan anchors re-derived against HEAD (see below)
16
17
 
18
+ ## Worktree cwd discipline
19
+
20
+ Run **before the first write/commit**. In a worktree session the process cwd can silently reset to the launch (main) checkout across a turn boundary; relative-path git ops then hit the WRONG checkout — and often **exit 0** (a stale local `main` HEAD == origin makes `git push origin HEAD:main` report "up-to-date"; `git add {worktree-only file}` fails but a sibling op succeeds), so the failure is silent.
21
+
22
+ Guard: read the recorded `lifecycle.worktree_path` from `state/milestone-{id}.yml`. Before the first write, compare it to `git rev-parse --show-toplevel`; on mismatch either `cd` back or — the robust default — **pin every git op with `git -C {worktree_path}`** (and use absolute paths for Write/Edit). The recorded path is authoritative (FORGE.md → "Recorded path is authoritative"). Same discipline the State Commit Protocol applies at handoff.
23
+
17
24
  ## Workspace Prerequisites
18
25
 
19
26
  Run **first, before the baseline snapshot** — a missing prerequisite makes the baseline's build/test commands fail for reasons that have nothing to do with the plan.
@@ -284,11 +291,12 @@ Once a task's verification bookkeeping settles (3-Strike result known, declared-
284
291
  | Context 40-60% | Consider fresh agent |
285
292
  | Context under 40% | Continue normally |
286
293
  | Switching unrelated subsystems | Spawn fresh agent |
287
- | Task carries ANY `complexity` tag (COST, M24) | Spawn a fresh executor on its resolved model — even under 40% context. Only an UNTAGGED glue task runs inline on the parent model. |
294
+ | Task carries a `complexity` tag whose resolved model **differs from the live session model** (COST, M24) | Spawn a fresh executor on that resolved model — even under 40% context (real routing). |
295
+ | Task carries a `complexity` tag but resolves to the **live session model** (e.g. no `models:` block) | Spawn changes nothing — treat the tag as a context signal only: spawn **iff** a row above fires. Otherwise run inline. |
288
296
 
289
- **Spawn via Agent tool** with: relevant plan details, specific files, locked decisions from context.md, clear success criteria. **Resolve the model PER TASK**, not once per skill run: `models.by_complexity.{task.complexity}` → `models.skills.executing` → `models.default` → parent (complexity overrides skill — FORGE.md → Model Routing). Display per spawn: *"Spawning executor with model: {model} (from {source})"*
297
+ **Spawn via Agent tool** with: relevant plan details, specific files, locked decisions from context.md, clear success criteria. **Resolve the model PER TASK**, not once per skill run: `models.by_complexity.{task.complexity}` → `models.skills.executing` → `models.default` → parent (complexity overrides skill — FORGE.md → Model Routing). **Then compare the resolved model to the live session model** (the model this session is actually running on): differ → spawn (mandatory routing); equal → the tag is a context signal, not a routing obligation. Display per spawn: *"Spawning executor with model: {model} (from {source})"*
290
298
 
291
- **Why the tag gates the spawn (enforced subagent spawning).** An inline task inherits the parent session's model and cannot be re-routedrouting only has teeth if every tagged task actually spawns. Spawning carries fixed briefing overhead, which is why the rule keys on the *tag* (a deliberate plan-time signal the work is worth routing) rather than "spawn everything": the cheapest untagged glue work stays inline. A full parent-as-orchestrator model (spawn everything, parent never implements) is a deferred follow-up, to be justified by M24 outcome-log evidence not implemented here.
299
+ **Why the tag gates the spawn (predicate A — the spawn must change the model).** Routing only *does* something when a task's resolved model differs from the **live session model**; a same-model spawn is pure briefing overhead m-30 burned ~315k subagent tokens spawning tasks that resolved straight back to the parent model for zero routing benefit. So the mandatory spawn fires only when resolving actually swaps the model (which requires a `models:` block whose resolution lands on a different tier than the one this session runs on). With **no `models:` block** every resolved model equals the parent == the live session model, so tagged tasks run inline unless a context trigger (20+ files / deep subsystem / >40%) independently calls for a fresh agent. The tag still marks work "worth routing," and an inline task genuinely cannot be re-routed — but that leak only matters when a different model was actually available, which is exactly the resolved-≠-live case the gate keeps mandatory. (A full parent-as-orchestrator model spawn everything, parent never implements remains a deferred follow-up, justified by M24 outcome-log evidence, not implemented here.)
292
300
 
293
301
  ## Execution Summary
294
302
 
@@ -348,5 +356,5 @@ After **both** layer plans are committed, the executing flow owns one final **se
348
356
  1. Confirm persistence — summary documented, commits made, state updated, desire-path files written
349
357
  2. **Run the Cross-Layer Seam Check** (above) if this phase was a Tier-2 contract split
350
358
  3. Set `current.status` to `verifying` in `.forge/state/milestone-{id}.yml`
351
- 4. **State-sync commit** (State Commit Protocol): `git add .forge/` then `git commit -m "chore(forge): sync state after executing — m{N} {phase-name}"`. Scoped — never `git add .`. Per-task code commits already landed; this captures the cursor + desire-path files at the phase boundary.
359
+ 4. **State-sync commit** (State Commit Protocol): `git -C {worktree_path} add .forge/` then `git -C {worktree_path} commit -m "chore(forge): sync state after executing — m{N} {phase-name}"` (worktree form — in a worktree session the recorded path is authoritative over cwd; see Worktree cwd discipline). Scoped — never `git add .`. Per-task code commits already landed; this captures the cursor + desire-path files at the phase boundary.
352
360
  5. Recommend: *"Tasks committed, state synced. `/clear` then `/forge` to continue with verifying."*
@@ -452,5 +452,5 @@ Done when approved.
452
452
  1. **Persist** -- plans `.forge/phases/`, reqs `.forge/requirements/m{N}.yml`, roadmap `.forge/roadmap.yml`, context `.forge/context.md`
453
453
  2. **Reserve version (if delivery bumps the version)** -- if any plan in this milestone will bump `package.json`/the project version, append ONE entry to `.forge/releases.yml` (Version Reservation Protocol, FORGE.md): pull first, set `version` = highest reserved `max()` bumped by this change's semver level, append `{milestone, version, bump, reserved_at, summary}`. **Never edit prior entries.** No version bump in scope → skip. Surface the reserved number to the user.
454
454
  3. **State** -- `current.status` = `executing` in `.forge/state/milestone-{id}.yml`
455
- 4. **State-sync commit** (State Commit Protocol): `git add .forge/` then `git commit -m "chore(forge): sync state after planning — m{N} {phase-name}"` (scoped; never `git add .`). The `releases.yml` reservation rides in this commit — **push it** so a parallel session sees the number taken before they reserve.
455
+ 4. **State-sync commit** (State Commit Protocol): `git -C {worktree_path} add .forge/` then `git -C {worktree_path} commit -m "chore(forge): sync state after planning — m{N} {phase-name}"` (worktree form — recorded `lifecycle.worktree_path` is authoritative over cwd; see FORGE.md State Commit Protocol. Scoped; never `git add .`). The `releases.yml` reservation rides in this commit — **push it** so a parallel session sees the number taken before they reserve.
456
456
  5. *"Plan written and synced. `/clear` then `/forge` to continue."*
@@ -75,7 +75,7 @@ Invoked via forge routing (mid-workflow or refactor-backlog item with milestone
75
75
  1. Read `.forge/state/milestone-{id}.yml` for current position
76
76
  2. Follow standard workflow (above)
77
77
  3. **If this task worked a refactor-backlog item:** in `.forge/refactor-backlog.yml` set that item's `status: done`, write `completed` (today, ISO) + `completed_by` (commit sha / one-line), THEN run the same **compaction-on-write** as `reviewing` Step 7 → "Status vocab + compaction-on-write" (normalize statuses → archive terminal items to `.forge/refactor-backlog-archive.yml` → leave only actionable items). Compaction is idempotent. (See reviewing Step 7 for the canonical rule — not repeated here.) This closes the loop: a worked item leaves the working backlog instead of lingering as `pending`.
78
- 4. After commit: update `milestone-{id}.yml` — advance the `current` cursor if the task moved position and set `current.last_updated`; log deviations if any Rule 1-3 applied. Do **not** write a progress percent (derived on read by `forge`) and do **not** write `index.yml` (derived). Then **state-sync commit**: `git add .forge/` && `git commit -m "chore(forge): sync state after quick-task — m{N}"` (scoped; never `git add .`).
78
+ 4. After commit: update `milestone-{id}.yml` — advance the `current` cursor if the task moved position and set `current.last_updated`; log deviations if any Rule 1-3 applied. Do **not** write a progress percent (derived on read by `forge`) and do **not** write `index.yml` (derived). Then **state-sync commit**: `git -C {worktree_path} add .forge/` && `git -C {worktree_path} commit -m "chore(forge): sync state after quick-task — m{N}"` (worktree form — recorded `lifecycle.worktree_path` is authoritative over cwd; see FORGE.md State Commit Protocol. Scoped; never `git add .`).
79
79
  5. Report: fix description, files changed, current position
80
80
 
81
81
  ### Without Milestone
@@ -128,7 +128,7 @@ Artifact uses the Finding Format above, with two adjustments: prepend `Date: {YY
128
128
 
129
129
  1. **Write artifact** (see Research Artifact section above).
130
130
  2. **Update state** — Set `current.status` to `discussing` in `.forge/state/milestone-{id}.yml`
131
- 3. **State-sync commit** (State Commit Protocol): `git add .forge/` then `git commit -m "chore(forge): sync state after researching — m{N} {phase-name}"` (scoped; never `git add .`).
131
+ 3. **State-sync commit** (State Commit Protocol): `git -C {worktree_path} add .forge/` then `git -C {worktree_path} commit -m "chore(forge): sync state after researching — m{N} {phase-name}"` (worktree form — recorded `lifecycle.worktree_path` is authoritative over cwd; see FORGE.md State Commit Protocol. Scoped; never `git add .`).
132
132
  4. **Recommend context clear:**
133
133
 
134
134
  *"Research complete. State synced. `/clear` then `/forge` to continue with discussing."*
@@ -32,6 +32,8 @@ Read: .forge/refactor-backlog.yml → existing backlog items (if any)
32
32
  Read: .forge/deferred-issues/*.md → pre-existing failures logged during execution (glob the dir; else legacy .forge/deferred-issues.md)
33
33
  ```
34
34
 
35
+ **Worktree cwd guard.** In a worktree session, before the state-sync commit (or any git op), compare `git rev-parse --show-toplevel` to the recorded `lifecycle.worktree_path`; on mismatch, pin every git op with `git -C {worktree_path}`. The cwd silently resets to the main checkout across turns and a wrong-cwd git op can exit 0 (silent). Same discipline the State Commit Protocol applies.
36
+
35
37
  Skip by stack: no DB->SQL/NoSQL N/A, no frontend->XSS N/A, no CI/CD->Pipeline N/A.
36
38
 
37
39
  Diff start: git log milestone start -> fallback: first commit after prev milestone -> ask user.
@@ -412,5 +414,5 @@ If the milestone's phases produced `contract.md` files (planning Step 6.1 Tier 1
412
414
  3. **Run promoted-milestone completion hook** (above) if `milestone.origin` set
413
415
  4. **Run Contract Landing** (above) for any cross-layer phases — fold ratified contracts into their ADRs
414
416
  5. Set `current.status: complete` and `current.completed_at: "<ISO 8601 timestamp>"` in `milestone-{id}.yml`, then regenerate `index.yml` via the `forge` **Rollup**
415
- 6. **State-sync commit** (State Commit Protocol): `git add .forge/` then `git commit -m "chore(forge): sync state after reviewing — m{N} complete"` (scoped; never `git add .`).
417
+ 6. **State-sync commit** (State Commit Protocol): `git -C {worktree_path} add .forge/` then `git -C {worktree_path} commit -m "chore(forge): sync state after reviewing — m{N} complete"` (worktree form — recorded path is authoritative over cwd; see Worktree cwd guard). Scoped; never `git add .`.
416
418
  7. *"Milestone [{name}] complete. Report: `.forge/audits/milestone-{id}-health-report.md`. {N} backlog items. `/forge` or backlog."*
@@ -24,6 +24,8 @@ Read: .forge/requirements/m{N}.yml → requirement IDs for coverage check (curre
24
24
  Read: .forge/deferred-issues/*.md → known pre-existing failures (glob the dir; else legacy .forge/deferred-issues.md; advisory)
25
25
  ```
26
26
 
27
+ **Worktree cwd guard.** In a worktree session, before any git op (test runs off the tree are fine; the state-sync commit + the checkpoint push are not), verify the process cwd: compare `git rev-parse --show-toplevel` to the recorded `lifecycle.worktree_path`; on mismatch, pin every git op with `git -C {worktree_path}`. The cwd silently resets to the main checkout across turns, and a wrong-cwd op can **exit 0** — e.g. a checkpoint `git push origin HEAD:main` reporting "up-to-date" against a stale local `main` HEAD. Same discipline the State Commit Protocol + strand guard apply.
28
+
27
29
  ## Deferred Issues
28
30
 
29
31
  Glob `.forge/deferred-issues/*.md` before running any tests (ADR-023 — one file per issue). If the
@@ -306,7 +308,7 @@ After PASSED verdict:
306
308
  1. **Human Verification Gate** — binding-dispatched (see "Human Verification Gate" above). Default (`in_session_signoff`): capture explicit sign-off and write `current.human_verified`; if the human answers **not yet** → STOP here; do not advance to reviewing. The milestone stays open until sign-off (or a recorded override) exists. `promotion_approval`: no capture — surface the relocation line and continue.
307
309
  2. **Persist** — Confirm verification report documented, desire-path files written to `.forge/state/desire-paths/`
308
310
  3. **Update state** — Set `current.status` to `reviewing` in `.forge/state/milestone-{id}.yml`
309
- 4. **State-sync commit** (State Commit Protocol): `git add .forge/` then `git commit -m "chore(forge): sync state after verifying — m{N} {phase-name}"` (scoped; never `git add .`).
311
+ 4. **State-sync commit** (State Commit Protocol): `git -C {worktree_path} add .forge/` then `git -C {worktree_path} commit -m "chore(forge): sync state after verifying — m{N} {phase-name}"` (worktree form — recorded path is authoritative over cwd; see Worktree cwd guard). Scoped; never `git add .`.
310
312
  5. **Integration Checkpoint Publish** (see below) — if this phase is a checkpoint and the session is a worktree, fast-forward-only publish to main.
311
313
  6. **Recommend clear:** *"State synced. `/clear` then `/forge` for reviewing."*
312
314
 
@@ -322,8 +324,10 @@ Realizes producer-side continuous integration (FORGE.md → Stream Integration C
322
324
 
323
325
  **Strand guard — never push past the operator's local main (forge#13).** `git push origin HEAD:main` is fast-forward-only *relative to `origin/main`* — but `origin/main` is **not** the operator's canonical checkout. The local `main` ref (shared across this machine's worktrees via the common git dir) can carry commits that were never pushed — another stream's merge, a framework bump, the state-sync commits from step 4. If you push the worktree tip onto an `origin/main` that is *behind* local main, origin fast-forwards but **local main is left diverged** (it has commits origin's new tip lacks, and vice versa). The closing report then mis-reads this as "local main is behind — `git pull`", and `git pull --ff-only` *fails* on divergence while a bare `git pull` makes a surprise merge commit — the exact failure the merge discipline exists to prevent. So gate the push on local main being in the publish's direct line:
324
326
 
327
+ **Worktree pin:** in a worktree session, prefix every `git` command in the blocks below with `git -C {worktree_path}` (recorded `lifecycle.worktree_path`). A reset cwd is exactly how a wrong-checkout `git push origin HEAD:main` silently reported "up-to-date" against a stale local `main` HEAD (the m-31 failure); the strand check itself (`rev-parse main` / `merge-base`) is equally meaningless run against the wrong tree. See Worktree cwd guard.
328
+
325
329
  ```bash
326
- git fetch origin main 2>/dev/null || true # refresh origin/main (no-op without a remote)
330
+ git -C {worktree_path} fetch origin main 2>/dev/null || true # refresh origin/main (no-op without a remote)
327
331
  # Publish is strand-safe only when local main is an ANCESTOR of this worktree's HEAD —
328
332
  # i.e. local main has no commits this checkpoint doesn't already contain.
329
333
  if git rev-parse --verify -q main >/dev/null 2>&1 \
@@ -334,7 +338,7 @@ fi
334
338
 
335
339
  - **Strand-safe** (local main is an ancestor of HEAD, or no local `main` ref exists) → publish **fast-forward only**:
336
340
  ```bash
337
- git push origin HEAD:main # remote present; git refuses a non-ff push (diverged origin surfaced, never forced)
341
+ git -C {worktree_path} push origin HEAD:main # worktree-pinned; git refuses a non-ff push (diverged origin surfaced, never forced)
338
342
  # no remote → fast-forward the local main ref where possible, else report ready-to-ff
339
343
  ```
340
344
  - **Success** → set the integration flag for **all** of this machine's checkouts (sibling worktrees *and* the primary main checkout): write `.git/forge/integration-pending` (in the git common dir) with the new main sha, this stream/milestone, and the shared surfaces this phase changed. Because local main was an ancestor, advancing it is a **true fast-forward** — report: *"Checkpoint published — phase {N} fast-forwarded onto main. Local main is now behind origin by this slice; it auto-fast-forwards on the next `/forge` boot, or `git merge --ff-only origin/main` from the primary checkout now."* (Here, and only here, is local main genuinely *behind* — the ff advice is correct.)
@@ -1,6 +1,6 @@
1
1
  # Forge
2
2
 
3
- Lean meta-prompting framework. Context engineering + constitutional governance on agent-native primitives.
3
+ Lean meta-prompting framework. Context engineering + constitutional governance on agent-native primitives. This file is the always-loaded **routing/index layer**: tables, invariants, and pointers. Procedures live in the skills that execute them; rationale lives in ADRs — both load only on demand.
4
4
 
5
5
  ## Critical: No Native Plan Mode
6
6
 
@@ -21,13 +21,11 @@ All phases: **invoke via `Skill` tool**, never native behavior. `planning` → `
21
21
 
22
22
  ## Core Protocol And Adapters
23
23
 
24
- **Core protocol** lives in `.forge/`: project/constitution state, milestones, requirements, roadmap, context, streams, work packages, plans, verification, audits, and state ownership rules. The core protocol describes durable behavior, not one platform's tool names.
25
-
26
- **Platform adapters** translate the protocol into a runtime. Each platform adapter maps core behavior to its tools: Claude Code skills/hooks, Codex/AGENTS behavior, manual multi-session handoffs, or future agent surfaces. Adapter mechanics stay in adapter files and skills; they should not become core requirements.
24
+ **Core protocol** lives in `.forge/`: project/constitution state, milestones, requirements, roadmap, context, streams, work packages, plans, verification, audits, and state ownership rules durable behavior, not one platform's tool names. **Platform adapters** translate the protocol into a runtime (Claude Code skills/hooks, Codex/AGENTS behavior, manual multi-session handoffs). Adapter mechanics stay in adapter files and skills; they do not become core requirements.
27
25
 
28
26
  ## Project Streams
29
27
 
30
- Forge has three runtime coordination layers: Project, Stream, and Work Package.
28
+ Three runtime coordination layers: Project, Stream, Work Package.
31
29
 
32
30
  | Layer | Purpose | Default Context |
33
31
  |---|---|---|
@@ -35,36 +33,31 @@ Forge has three runtime coordination layers: Project, Stream, and Work Package.
35
33
  | **Stream** | One active line of work: goal, branch/worktree, state, blockers, next action | `.forge/streams/{stream}.yml` + `brief.md` |
36
34
  | **Work Package** | Delegated bounded task inside a stream | `.forge/streams/{stream}/packages/{id}.yml` |
37
35
 
38
- Use ordinary Quick/Standard/Full tiers inside a stream. Use Project Chief / Chief of Staff when coordinating multiple active streams or pivoting without dropping context. Workers never coordinate laterally; they report to the stream or project chief. M10 worktrees remain an optional backend for isolation/merge discipline, not the primary UX.
36
+ Ordinary Quick/Standard/Full tiers run inside a stream. Project Chief / Chief of Staff coordinates multiple active streams or pivots without dropping context. Workers never coordinate laterally; they report to the stream or project chief. M10 worktrees are an optional isolation backend, not the primary UX.
39
37
 
40
- **Merge often and early.** Integrate ready work **as soon as it is ready**, not at milestone end. Long-lived stream branches drift from main, shared surfaces go stale, and the merge gets larger and riskier the longer it waits — the opposite of the merge discipline streams exist to provide. The default posture is continuous integration, realized two ways: **producer-side** by publishing at verified checkpoints (below), and **project-side** by the Chief's Merge Cadence Check (cross-stream order + streams without checkpoints). Forge never force-pushes, never auto-resolves a conflict, and never auto-rebases.
38
+ **Merge often and early.** Integrate ready work **as soon as it is ready**, not at milestone end — long-lived stream branches drift and the merge grows riskier the longer it waits. Realized producer-side (checkpoint publishing, below) and project-side (the Chief's Merge Cadence Check). Forge never force-pushes, never auto-resolves a conflict, and never auto-rebases.
41
39
 
42
40
  ### Stream Integration Checkpoints
43
41
 
44
- Integration is **event-driven, not polled**: a stream publishes its work to main at plan-marked phase boundaries, and a lightweight flag tells sibling worktrees there is something to pull. No flag set → boot does no integration work.
42
+ Event-driven continuous integration. **Mechanism + rationale: [ADR-027](../docs/decisions/ADR-027-integration-checkpoints-declaration-split.md).** Producer implementation: `verifying` (Integration Checkpoint Publish). Consumer: `forge` boot (Step 1 preflight). In brief:
45
43
 
46
- - **Checkpoints (plan-marked).** `planning` marks selected phases as integration checkpoints (`integration_checkpoint: true` in the plan frontmatter) the points where a verified slice should land on main. Default heuristic: the last phase, plus any phase another stream/phase depends on. Advisoryplanning proposes, the operator confirms. **The mark is the opt-in:** a plan with no marked checkpoint never auto-publishes, so existing/unmarked work is unaffected.
47
- - **Producer — publish on verified checkpoint (fast-forward only, strand-guarded).** When `verifying` returns PASS for a checkpoint phase *and the session is a worktree*, it integrates that phase to main with a **fast-forward-only** push: `git push origin HEAD:main`. Git rejects a non-ff push (vs `origin/main`) — that surfaces a diverged *origin*, never force-pushed. But ff-vs-origin is not enough on its own: `origin/main` is not the operator's canonical checkout, and the local `main` ref can carry commits origin never saw (another stream's merge, a framework bump, state-sync commits). Pushing past a *behind* origin then leaves **local main diverged, not behind** — and the naive "you're behind, `git pull --ff-only`" advice *fails* on divergence (forge#13). So the producer **strand-guards** first: it publishes only when local `main` is an **ancestor of the worktree HEAD** (no local commits this checkpoint omits). Stranded (local main has such commits) it does **not** push; it surfaces "integrate through local main first" and leaves the work on the branch. On a strand-safe success it sets the **integration flag**. (No remote → fast-forward local `main` where possible, else record ready-to-ff for the primary checkout.)
48
- - **Producer — publish at intake (declaration split, second trigger for the same mechanic).** Milestone/stream intake (discussing's Milestone Gate, `forge` Step 1.2 backlog promotion, `prototyping` graduation) reuses this exact strand-guard + fast-forward + integration-flag mechanic, triggered at a different moment: not a `verifying` PASS, but the worktree's first commit. Before any content (the full `milestone-{id}.yml`, research, discussion, plans, code) is written, the worktree commits a **declaration** — milestone ID (+ reservation), one-line intent, stream registration, and coordination/touches claims, i.e. `.forge/streams/{stream}.yml` with `ownership.owned_surfaces`/`shared_surfaces` populatedand fast-forward-pushes it to `origin/main` immediately. Content stays worktree-only until the milestone's own checkpoint (above). This keeps `chief-of-staff`'s cross-stream conflict detection — which globs `.forge/streams/*.yml` from the current checkout only — sighted on in-flight work without teaching it to read across worktrees (rejected: this project spans machines, so cross-worktree reading needs the early-push step *plus* multi-branch readers — strictly more machinery). One-breath rule: main knows THAT work exists; the worktree knows WHAT it is.
49
- - **Flag (cheap, set by the producer).** A marker in the shared git common dir — `.git/forge/integration-pending` records the new main sha, the publishing stream, and the shared surfaces that changed. It is shared across all of a repo's worktrees on a machine, so any boot reads it for free; absent or already-seen → skip. The `main` ref is the source of truth the marker points at. Cross-machine (the marker is machine-local) falls back to the opt-in `forge.worktree_rebase_check` / a periodic fetch eventual, not instant.
50
- - **Consumer — auto fast-forward, else prompt (worktrees *and* the primary main checkout).** `forge` boot checks the flag in **both** checkout kinds. In a **worktree**: flag set (main advanced past this worktree's merge-base) and no divergent local commits → **fast-forward automatically** (`git merge --ff-only main`); diverged → **surface a rebase prompt** (never auto-rebase). In the **primary main checkout**: the strand guard guarantees local main is a clean ff of the new `origin/main`, so boot **fast-forwards local main automatically** (`git merge --ff-only origin/main`) — this is what keeps the operator's canonical checkout in lockstep with origin after every checkpoint, instead of drifting behind until the next local commit diverges it. Flag not set → no fetch, no scan, continue boot.
44
+ - **Checkpoints are plan-marked** (`integration_checkpoint: true` in plan frontmatter; planning proposes last phase + depended-on phases — the operator confirms). **The mark is the opt-in**: unmarked plans never auto-publish.
45
+ - **Producer:** on a checkpoint phase's verified PASS in a worktree session, fast-forward-only publish (`git push origin HEAD:main`), **strand-guarded** publish only when local `main` is an ancestor of the worktree HEAD; strandeddo not push, surface "integrate through local main first" (forge#13). On success, set the **integration flag** `.git/forge/integration-pending` in the machine-local git common dir.
46
+ - **Intake (declaration split, B3'):** a new milestone/stream's **first** worktree commit is a declaration — milestone id (+ reservation), one-line intent, stream registration with `ownership` claimsff-pushed to main immediately; content stays worktree-only until the milestone's own checkpoint. One-breath rule: main knows THAT work exists; the worktree knows WHAT it is.
47
+ - **Consumer:** boot reads the flag (absent or already-seen skip; no fetch, no scan). Clean fast-forwards run automatically in worktrees **and** the primary main checkout; any divergence surfaces a prompt never an auto-rebase. Cross-machine, the flag doesn't exist the opt-in `forge.worktree_rebase_check` / periodic fetch covers it, eventually.
51
48
 
52
49
  ### Worktree Convention (base)
53
50
 
54
- Worktrees are a **base concern** — Chief/Streams creates and references them with plain `git worktree`, independent of the experimental `orchestrating` skill. This convention is the single source of truth for *where worktrees live and what they're named*, so it lives here in base (not buried in an experimental skill) and stays upgrade-synced for every install (see [ADR-010](../docs/decisions/ADR-010-repo-scoped-worktree-root.md)).
51
+ Where worktrees live and what they're named single source of truth, base concern ([ADR-010](../docs/decisions/ADR-010-repo-scoped-worktree-root.md)). Chief/Streams uses plain `git worktree`.
55
52
 
56
53
  | Element | Convention |
57
54
  |---|---|
58
- | **Root** | `orchestration.worktree_root` from `project.yml`; if unset, default `../<repo-basename>-worktrees/`. Relative paths resolve against the **repo root** (not shell cwd); absolute and leading-`~` paths are honored verbatim. Per-repo sibling dirs keep each repo's worktrees from pooling into one shared dir. |
59
- | **Anchor** | `{milestone-id}-{session}` (e.g. `m-9-a1b2c3d4`). For a manual stream worktree with no session, the milestone/stream id alone is acceptable (`m-PLUG01`). |
60
- | **Path** | `{worktree_root}/{anchor}` — the resolved **absolute** path. |
55
+ | **Root** | `orchestration.worktree_root` from `project.yml`; default `../<repo-basename>-worktrees/`. Relative paths resolve against the **repo root**; absolute / `~` honored verbatim. |
56
+ | **Anchor** | `{milestone-id}-{session}` (e.g. `m-9-a1b2c3d4`); milestone/stream id alone acceptable for a manual stream worktree. |
57
+ | **Path** | `{worktree_root}/{anchor}` — resolved **absolute**. |
61
58
  | **Branch** | `forge/{anchor}`. |
62
59
 
63
- **Recorded path is authoritative once set.** When a worktree is created through `orchestrating`, the resolved absolute path is recorded in `lifecycle.worktree_path` (in `state/milestone-{id}.yml`) or in a stream's `runtime.worktree`, and every later operation reads it verbatim never re-derives. Changing `orchestration.worktree_root` later only affects **new** worktrees; live ones keep working off their recorded path. Move a live worktree only with `git worktree move` (a bare `mv` strips the gitdir pointer), then update the recorded path.
64
-
65
- **Fallback when no path was recorded.** A worktree created outside `orchestrating` (a manual `git worktree add`, an adopted stream) may have no recorded path. Resolve it from this convention — `{worktree_root}/{anchor}` — rather than assuming the experimental skill's internal default. Chief/Streams uses this fallback; `orchestrating` (when installed) implements the same scheme for the worktrees it creates.
66
-
67
- **Validated against the convention (advisory).** "Authoritative" means *honored*, not *unchecked*: `forge` boot (Step 1 preflight) and `chief-of-staff` adoption compare a recorded path's parent dir to the resolved `worktree_root` and **warn** — never block, never move — when it sits outside. This catches a manual `git worktree add ../forge-worktrees/m115` or pre-convention state that the verbatim-trust rule would otherwise sail past (the convention was a *fallback resolver* only, never a *validator*). The warning honors an explicit `orchestration.worktree_root`. Fix with `git worktree move` + update the recorded path.
60
+ **Recorded path is authoritative once set** `lifecycle.worktree_path` (milestone) / `runtime.worktree` (stream) is read verbatim, never re-derived; changing `worktree_root` affects only new worktrees; move a live worktree only with `git worktree move` + update the record. No recorded path resolve from the convention. `forge` boot and `chief-of-staff` adoption **validate advisorily**: recorded path outside the resolved root → warn (never block, never move); an explicit `worktree_root` is honored.
68
61
 
69
62
  ## Workflow Tiers
70
63
 
@@ -73,7 +66,7 @@ Auto-detects complexity. Override: "Use Quick/Standard/Full tier."
73
66
  ### Prototyping (minutes–hours, disposable)
74
67
  **Triggers:** "mock up"/"sketch"/"prototype"/"draw up"/"iterate on the UI"/"explore how X could look" — disposable in-app visual exploration behind an alpha/feature flag, no requirements locked yet
75
68
  **Flow:** `prototyping` — establish flag → iterate ⟲ (render → react → adjust) → resolve (graduate→milestone | park | discard)
76
- **No verify/review gate by design.** The deliverable is a thing to look at and react to, not shippable code. Fills the gap where Standard/Full ceremony (goal-backward verification, a milestone cursor) is the wrong shape and Quick's <50-line/1-2-file caps + commit-and-done don't fit an iteration loop. Real UI work with known requirements is Standard + `designing` (which *does* gate on quality). See [ADR-022](../docs/decisions/ADR-022-prototyping-tier.md).
69
+ **No verify/review gate by design** the deliverable is a thing to look at and react to, not shippable code. Real UI work with known requirements is Standard + `designing` (which *does* gate on quality). See [ADR-022](../docs/decisions/ADR-022-prototyping-tier.md).
77
70
 
78
71
  ### Quick (minutes)
79
72
  **Triggers:** bug fix, typo, config, dep bump, <50 lines
@@ -85,7 +78,7 @@ Auto-detects complexity. Override: "Use Quick/Standard/Full tier."
85
78
 
86
79
  ### Full (days)
87
80
  **Triggers:** new project, major milestone, complex multi-system, architectural decisions
88
- **Flow:** `researching` `discussing` `architecting` `planning` `executing` → `verifying` → `reviewing` → done
81
+ **Flow:** Standard flow + `architecting` between discussing and planning
89
82
  **Optional:** `designing` (UI), `securing` (auth/data/API), `debugging` (stuck), `testing` (e2e/integration/flake)
90
83
 
91
84
  ### Advisory (tier deferred)
@@ -103,13 +96,13 @@ Auto-detects complexity. Override: "Use Quick/Standard/Full tier."
103
96
  | Architectural decisions | `architecting` | Full |
104
97
  | Break work into tasks with gates | `planning` | Standard, Full |
105
98
  | Build with deviation rules + atomic commits | `executing` | All |
106
- | Prove work delivers on goals (+ M9 e2e validation gate when `e2e:true` stories present; captures the Human Verification Gate sign-off) | `verifying` | Standard, Full |
107
- | Audit health + catalog refactoring (+ M9 e2e soft-cap, orphan-test, flake-rate audits; hard-blocks completion on the Human Verification Gate) | `reviewing` | Standard, Full |
99
+ | Prove work delivers on goals (+ M9 e2e validation gate; captures the Human Verification Gate sign-off) | `verifying` | Standard, Full |
100
+ | Audit health + catalog refactoring (+ M9 e2e audits; hard-blocks completion on the Human Verification Gate) | `reviewing` | Standard, Full |
108
101
  | Small scoped fix | `quick-tasking` | Quick |
109
102
  | Rapid disposable UI mockup behind an alpha flag (no verify/review gate) | `prototyping` | Prototyping |
110
103
  | UI with design system | `designing` | When UI |
111
104
  | Security review | `securing` | When auth/data/API |
112
- | E2E/integration tests + suite audit (+ M9 author-mode gate refuses e2e without `e2e:true` + `validated:true`) | `testing` | When UI/flows or flaky suite |
105
+ | E2E/integration tests + suite audit (+ M9 author-mode gate) | `testing` | When UI/flows or flaky suite |
113
106
  | Systematic debugging | `debugging` | When stuck |
114
107
  | Upgrade Forge files | `upgrading` | On-demand |
115
108
  | Cross-session memory | `beads-integration` | When Beads installed |
@@ -123,86 +116,47 @@ Auto-detects complexity. Override: "Use Quick/Standard/Full tier."
123
116
  ### Size Gates
124
117
  | Artifact | Max | Reason |
125
118
  |----------|-----|--------|
119
+ | `FORGE.md` | 35 KB | Claude Code truncates memory files at 40k chars; routing-layer target ~28k |
126
120
  | `project.yml` | 5 KB | Forces clarity |
127
121
  | `requirements/m{N}.yml` | 50 KB/file | Prevents scope creep |
128
122
  | `plan.md` | 30 KB | Keeps executor context <50% |
129
123
  | `constitution.md` | 10 KB | Gates must be scannable |
130
124
  | `context.md` | 12 KB | Active decisions only; history archives to `context-archive.md` |
131
- | `refactor-backlog.yml` | 150 KB | Working backlog stays scannable; terminal items archive to `refactor-backlog-archive.yml` (advisory — flags, never blocks) |
132
- | `streams/active.yml` | 20 KB | Project Chief traffic map must stay cheap to load |
125
+ | `refactor-backlog.yml` | 150 KB | Terminal items archive to `refactor-backlog-archive.yml` (advisory) |
126
+ | `streams/active.yml` | 20 KB | Traffic map must stay cheap to load |
133
127
  | `streams/{stream}.yml` | 15 KB | Stream state stays resumable without becoming history |
134
- | `streams/{stream}/brief.md` | 8 KB | Stream context packet must fit into active session context |
128
+ | `streams/{stream}/brief.md` | 8 KB | Context packet must fit into active session context |
135
129
  | `streams/{stream}/packages/{id}.yml` | 10 KB | Delegated work contract stays bounded |
136
- | `reservations.yml` | 50 KB | Append-only ID-reservation registry; one short line per ADR/DEF/FR/NFR reserved |
137
- | `prototypes/{slug}.md` | 8 KB | Disposable-mockup state — current question + terse iteration log, not a design doc; terminal items archive to `prototypes/archive/` |
130
+ | `reservations.yml` | 50 KB | One short line per reserved id |
131
+ | `prototypes/{slug}.md` | 8 KB | Current question + terse iteration log; terminal items archive |
138
132
 
139
133
  ### Accumulating Artifacts
140
134
 
141
- Every new accumulating artifact needs: owner, size gate, archive/compaction rule,
142
- `derived` vs source-of-truth status, and migration behavior. Defaults:
143
-
144
- - `roadmap.yml`, `requirements/*.yml`, `releases.yml`, `reservations.yml` are
145
- source-of-truth; load scoped slices, not whole history.
146
- - `phases/`, `audits/`, `research/`, `archive/`, and `context-archive.md` are
147
- historical; load only by milestone/stream/package/version.
148
- - `prototypes/{slug}.md` is single-owner mutable while `status: exploring | parked`
149
- (the driving session writes it); terminal statuses (`graduated`, `discarded`)
150
- archive to `prototypes/archive/`. `forge` boot surfaces stale `parked` prototypes
151
- (>30d). Not derived, not shared — a prototype has exactly one driver.
152
- - Runtime/generated files (`.mcp-server/*.db*`, logs, spike DBs, and the
153
- `.git/forge/*` machine-local files — the id-reservation ledger + integration
154
- flag) are excluded from framework context and packages unless debugging them.
155
- - The board (ADR-025): `.forge/notion/map.yml` is **derived** (owner: main/orchestrator
156
- session; `forge-id → page-id` cache), **gitignored**, and rebuildable by query-self-heal
157
- (ADR-020) — never committed, never hand-edited, no size gate. The board **config** is the
158
- `board:` block in `project.yml` — **stable-shared**, governed by project.yml's 5 KB gate,
159
- changes via `/upgrading`; migration is lazy (absent block / `enabled:false` ⇒ inert, zero
160
- behavior change). The board itself accumulates nothing in-repo — it is a projection + inbox.
161
- - Template copies sync mechanically; do not treat them as separate concepts.
135
+ Every new accumulating artifact declares: owner, size gate, archive/compaction rule, `derived` vs source-of-truth status, and migration behavior. Defaults: `roadmap.yml`, `requirements/*.yml`, `releases.yml`, `reservations.yml` are source-of-truth — load scoped slices, not whole history. `phases/`, `audits/`, `research/`, `archive/`, `context-archive.md` are historical — load only by milestone/stream/package/version. `prototypes/{slug}.md` is single-owner mutable while `exploring|parked`; terminal statuses archive to `prototypes/archive/`. Runtime/generated files (`.mcp-server/*.db*`, logs, spike DBs, `.git/forge/*`) are excluded from framework context unless debugging them. The board (ADR-025): `.forge/notion/map.yml` is derived, git-ignored, rebuildable; board config is the `board:` block in `project.yml` (absent ⇒ inert). Template copies sync mechanically — not separate concepts.
162
136
 
163
137
  ### Fresh Agent Pattern
164
138
  Task touches 20+ files or complex subsystem → spawn fresh executor with isolated context. Prevents context rot.
165
139
 
166
140
  ### Context Handoff
167
- Each phase writes outputs to `.forge/` before completing. At phase boundaries, recommend `/clear`. Next phase loads from disk. Advisory — skip for short phases under 40% context.
141
+ Each phase writes outputs to `.forge/` before completing. At phase boundaries, recommend `/clear`; the next phase loads from disk. Advisory — skip for short phases under 40% context.
168
142
 
169
143
  ### Lazy Loading
170
- Skills load only when invoked. Active context is `.forge/context.md`; consult
171
- `.forge/context-archive.md` only when older decisions matter. Skill details stay
172
- on demand. Base context ~300 lines.
144
+ Skills load only when invoked. Active context is `.forge/context.md`; consult `.forge/context-archive.md` only when older decisions matter.
173
145
 
174
146
  ### Rules Layer (`.claude/rules/`)
175
- Three policy weights, lightest-first (ADR-018):
176
- - **`constitution.md`** — always-loaded architectural **gates** (heavy, project-wide, hard rules; size-gated 10 KB).
177
- - **`.claude/rules/`** — lightweight ambient guidance. Always-on (no frontmatter, loads every session) or path-scoped (`paths:` glob frontmatter, auto-attaches only when editing a matching file — **zero context cost until you touch that code**).
178
- - **Skills** — heavy, opt-in, loaded by task description.
179
-
180
- Core ships one always-on rule: **`agent-discipline.md`** (the stack-agnostic AI failure-mode floor; restates the everyday version of the executor's Deviation Rules). **Stack-specific** path-scoped rules (Laravel/Vue/Python) and convention skills ship in the **opt-in `experimental/conventions/` pack**, not core — keeping the base stack-neutral. Authoring: keep each rule under ~20 lines, imperative-first; if it grows past ~25 lines it is a skill in disguise. See `.claude/rules/README.md`.
147
+ Three policy weights, lightest-first (ADR-018): **`constitution.md`** (always-loaded hard gates, 10 KB) → **`.claude/rules/`** (lightweight ambient guidance — always-on, or path-scoped via `paths:` glob at zero cost until a matching file is touched) → **skills** (heavy, opt-in). Core ships one always-on rule, `agent-discipline.md`; stack-specific rules are opt-in (`experimental/conventions/`). Format + authoring: `.claude/rules/README.md` (itself always loaded).
181
148
 
182
149
  ## Model Routing
183
150
 
184
- Configure in `project.yml`:
151
+ Configured in `project.yml` under `models:` — `default`, `parent_session` (advisory), `by_complexity.{trivial|standard|complex}` (per-task, M24), `skills.{name}` (per-skill).
185
152
 
186
- ```yaml
187
- models:
188
- default: sonnet # fallback
189
- parent_session: sonnet # advisory — warns on mismatch
190
- by_complexity: # per-task complexity → model (M24). Overrides skills — see Precedence.
191
- trivial: haiku
192
- standard: sonnet
193
- complex: opus
194
- skills: # per-skill overrides
195
- architecting: opus
196
- planning: opus
197
- verifying: haiku
198
- quick-tasking: haiku
199
- ```
153
+ **Precedence:** `by_complexity.{task.complexity}` → `skills.X` → `default` → parent model. **Complexity overrides skill** (LOCKED, context.md § M24): a task's `by_complexity` resolution wins over `models.skills.{skill}` — the per-task signal is the granular one; a task with no complexity resolves via the unchanged chain, zero behavior change untagged.
200
154
 
201
- **Precedence:** `by_complexity.{task.complexity}` `skills.X` `default` parent model.
202
- **Complexity overrides skill** (LOCKED, context.md § M24): when a task declares a `<complexity>`, its `by_complexity` resolution wins over `models.skills.{skill}`. WHY — the per-task signal is the granular one and the whole point of M24; letting the coarser skill-level setting win would make per-task routing inert on any project that already sets `models.skills.executing`. A task with **no** complexity resolves via the unchanged `skills → default → parent` chain — zero behavior change for untagged tasks.
203
- **All advisory** — forge displays expected model at each transition, suggests `/model` on mismatch. Review gate logs model used. Cannot auto-switch.
155
+ **All advisory** forge displays the expected model at each transition and suggests `/model` on mismatch; the review gate logs the model used. Forge cannot auto-switch models.
204
156
 
205
- **Subagent inheritance.** When a skill spawns subagents via the Agent tool (parallel research, fresh-context reviewers, executor fan-out, tester author/analyst, etc.), the skill MUST pass the resolved `model` param explicitly. Subagents do NOT inherit skill-level routing automatically — without the param they run on the parent session's model, defeating the routing intent. Resolution is **per-task** where the task being spawned declares a `complexity` (`by_complexity` wins per the Precedence above), falling back to the skill-level chain (`models.skills.{skill}` → `models.default` → parent) otherwise — surface it: *"Spawning {role} with model: {model} (from {source})"*. **Inline-task limitation:** a task that runs inline (no spawn) inherits the parent session's model and cannot be re-routed — per-task routing is inherently a subagent-spawn feature, not something that reaches into an already-running skill session.
157
+ **Subagent inheritance.** A skill spawning subagents MUST pass the resolved `model` param explicitly subagents do NOT inherit skill-level routing (without the param they run on the parent model, defeating the routing intent). Resolve **per task** where the task declares `complexity`, else the skill chain; surface *"Spawning {role} with model: {model} (from {source})"*. An inline task inherits the session model and cannot be re-routed.
158
+
159
+ **Spawn gate — predicate A (LOCKED, context.md § M24; refined m-33).** Executing's mandatory spawn on a complexity-tagged task fires **only when the resolved model differs from the live session model**. Resolved == live — always the case with no `models:` block — makes the spawn a pure no-op, so the tag becomes a context signal only (spawn iff a context trigger independently fires). WHY: an unconditional spawn burned ~315k subagent tokens on m-30 for zero routing benefit.
206
160
 
207
161
  ## Agents
208
162
 
@@ -213,12 +167,12 @@ models:
213
167
  | `executor` | Building + deviation rules | All dev tools | Execution |
214
168
  | `verifier` | Goal-backward verification | Read + Bash (tests) | Verification |
215
169
  | `reviewer` | Security + architecture + refactoring (breadth pass, gates the health report) | Read, Bash, Grep, Glob | Reviewing |
216
- | `tester` | E2E/integration authoring + suite analysis | Dual-mode: author (Write/Edit/Bash/git) · analyst (Read/Glob/Grep/Bash read-only) | Testing skill |
217
- | `security-reviewer` | Confidence-gated security depth (≥8, exploit-scenario required) | Read, Grep, Glob, Bash | Optional deep-dive (from `reviewing` or direct) |
170
+ | `tester` | E2E/integration authoring + suite analysis | Dual-mode: author (Write/Edit/Bash/git) · analyst (read-only) | Testing skill |
171
+ | `security-reviewer` | Confidence-gated security depth (≥8, exploit-scenario required) | Read, Grep, Glob, Bash | Optional deep-dive |
218
172
  | `performance-reviewer` | Real bottlenecks only (Impact = frequency × cost; ≥8 + ≥Medium) | Read, Grep, Glob, Bash | Optional deep-dive |
219
173
  | `doc-reviewer` | Docs-vs-source accuracy (≥8; Blocker/Major/Minor) | Read, Grep, Glob, Bash | Optional deep-dive |
220
174
 
221
- The three specialist reviewers **complement** the generalist `reviewer` — single-axis, defensible depth, read-only. They are pulled in deliberately (the `reviewing` skill can spawn them, or invoke directly); the 3-mode `reviewer` remains the breadth pass that gates the milestone health report.
175
+ The three specialist reviewers complement the generalist `reviewer` — single-axis, defensible depth, read-only, pulled in deliberately (from `reviewing` or direct). The 3-mode `reviewer` remains the breadth pass that gates the milestone health report.
222
176
 
223
177
  ## Project Init (First Run)
224
178
 
@@ -232,114 +186,75 @@ Quick tier skips init.
232
186
 
233
187
  ## State Management
234
188
 
235
- State lives in `.forge/`:
236
- - `project.yml` — Vision, stack, design system, verification, constraints (<5KB)
237
- - `constitution.md` — Active architectural gates
238
- - `design-system.md` — Component mapping table
239
- - `requirements/m{N}.yml` — Per-milestone structured requirements with `[NEEDS CLARIFICATION]` markers. **FR-IDs, DEF-IDs, and NFR-IDs are globally unique across all milestone files** — `FR-001` may exist in exactly one `m{N}.yml`. Before adding a new ID, **reserve it via the ID Reservation Protocol** run `forge-reserve fr|nfr|def --milestone <id>` and use the id it prints (the helper dual-writes the machine-local ledger + `.forge/reservations.yml`; you commit `reservations.yml` with the work). The next number is `max(ledger ∪ in-tree ∪ main:reservations.yml) + 1`. The machine-local ledger is what makes the shared ID space safe across **concurrent worktrees**: bare scan-and-increment (or a branch-local `reservations.yml`) sees only committed/merged state, so two worktrees branched from one baseline claim the same number and collide silently until merge (see ADR-021, supersedes ADR-016). On a residual collision (legacy/un-reserved, e.g. during a migration), keep the older milestone's ID and renumber the newer. Concurrent milestones each own their file — no cross-stream contention on file writes, but ID space is shared. Functional requirements may carry M9 e2e gate fields (`e2e`, `observable_outcome`, `observable_outcome_hash`, `validated`) — lazy migration, absent fields default to `e2e:false`/`validated:false`.
240
- - `roadmap.yml` — Phases, milestones, dependencies
241
- - `state/index.yml` — DERIVED registry rolled up from milestone files (id, name, status, last_updated). Never hand-edited; never written by worktree agents. **Git-ignored render-on-read cache (0.53.0): regenerated at every boot from the milestone files, never committed** — so a committed projection can never drift stale from its source, and any session can render it locally without touching `main`. Absent on a fresh clone until the first boot renders it (the project sentinel is `project.yml`, not this file). The boot rollup also renders each unit's **derived `release_state`** into this cache (see below).
242
- - **Derived `release_state` (0.56.0, [ADR-024](../docs/decisions/ADR-024-merged-validated-fold.md))**a unit's `release_state ∈ {unmerged, merged, validated}` is **computed at rollup, never stored** (never an enum on `current.status`, never a committed field) and rendered only into the git-ignored cache. `merged` = the unit's landed SHA is an ancestor of the mainline; `validated` requires `merged` plus the repo's configured approval. Computed by the fold-of-record `.claude/hooks/forge-release-fold.sh` (git-only, offline, main-checkout-resolvable all field reads via `git show main:`). Validation is dispatched on `forge.validation_event` in `project.yml`: `in_session_signoff` (**default**, absent ⇒ this — `human_verified`/M0 signoff, legacy-complete grandfathered) or `promotion_approval` (a CI-signed `promotion/{sha}` tag verified offline via `git tag -v` + allowed-signers, validated by containment). Inert by default — every repo unchanged until it opts in. The gate-relocation consequence lives in the Human Verification Gate section; the full spec lives in ADR-024 + the fold header (not duplicated here — Article XII).
243
- - `state/milestone-{id}.yml` — Per-milestone cursor (single source of truth): position, progress, decisions, blockers. One owner at a time.
244
- - `state/desire-paths/`Append-only framework-usage observations, one file per observation. Occurrence counts derived by globbing (no mutable counter). Each carries `scope: project | framework` (set at capture; absent ⇒ project). **`forge` boot is the single review owner**: at 3+ occurrences it surfaces a concrete fix from the co-located suggestion table, persists declines to `declined/` (no re-nag), and for `scope: framework` signals routes them upstream — a portable block to `upstream/` plus a `gh issue` offer against `forge.upstream_repo`. This closes Principle 7's capture→act loop; `verifying` captures but no longer surfaces. See [ADR-012](../docs/decisions/ADR-012-upstream-desire-path-feedback-channel.md).
245
- - `context.md` — Active locked decisions + deferred ideas (target <=12KB)
246
- - `context-archive.md` — Historical locked decisions; load only when relevant
247
- - `streams/active.yml` — **DERIVED** Project Chief traffic map. Regenerated by the Stream Rollup from the per-stream files + active milestone files (see Stream Rollup below). **Never hand-edited**, like `index.yml`, and — since 0.53.0 — a **git-ignored render-on-read cache** regenerated at every boot, never committed (this is the durable kill for the drift where a committed `active.yml` phase went stale against its milestone). Each row carries the two orthogonal dimensions side by side: `phase` (from the milestone, for milestone-backed streams) and `coordination` (from the stream file). See [ADR-015](../docs/decisions/ADR-015-derived-stream-registry.md).
248
- - `streams/{stream}.yml` — Per-stream **source of truth**: coordination lifecycle, ownership, shared surfaces, blockers, merge readiness, and an optional `stream.milestone: m-{id}` link. Standalone (quick-fix) streams own their `status` here; milestone-backed streams omit workflow phase (the rollup reads it from the milestone).
249
- - `streams/{stream}/brief.md` — Human-readable stream context packet loaded when resuming that stream.
250
- - `streams/{stream}/packages/{id}.yml` — Work package contract for delegated workers or manual sessions.
251
- - `research/milestone-{id}.md` — Research findings snapshot (dated, immutable)
252
- - `phases/milestone-{id}/{phase}-{name}/plan-{NN}.md` — Task plans with must_haves frontmatter (`{id}`=milestone, `{phase}`=phase# preserved verbatim, `{name}`=kebab, `{NN}`=seq)
253
- - `refactor-backlog.yml` — Refactoring catalog (actionable items only), worked via quick-tasking. Canonical status vocab: `pending | in_progress | done | dismissed | deferred`. Terminal items auto-archive on the next reviewing/quick-tasking write (compaction-on-write).
254
- - `refactor-backlog-archive.yml`Append-only terminal-item archive (`done`/`dismissed`); keeps the audit trail out of the working backlog.
255
- - `releases.yml` — Append-only version-reservation registry (cross-session coordination point; see Version Reservation Protocol)
256
- - `reservations.yml` — Append-only ID-reservation audit trail + cross-machine floor for ADR/DEF/FR/NFR numbers, written by `forge-reserve` (ADR-021). The same-machine cross-worktree coordination point is the machine-local ledger `.git/forge/id-reservations`; see ID Reservation Protocol.
257
- - `archive/milestone-{id}/`Archived milestone artifacts (archive-delete)
258
- - `prototypes/{slug}.md` — Lightweight resumable state for a disposable in-app design mockup (Prototyping tier). Status vocab: `exploring | parked | graduated | discarded`. Terminal (`graduated`/`discarded`) → archive to `prototypes/archive/{slug}.md`. Single-owner mutable; not derived, not shared. See `Skill(prototyping)`.
259
-
260
- **Milestones** group phases into concurrent streams. Own state file — no conflicts across sessions. Can be deferred (frozen in place) or archive-deleted.
261
- `index.yml status` gates routing: `not_started | active | deferred | complete`.
262
-
263
- **Stream Rollup (`active.yml` is derived).** Mirrors the milestone Rollup: `active.yml` is a pure projection, regenerated never hand-edited — by a deterministic, idempotent **join** of two single-sourced inputs (ADR-015). Run by the main/orchestrator session and at `chief-of-staff` Show/Sync; worktrees never write it. Procedure:
264
- 0. **Completed-stream auto-close sweep (source write, main checkout only — runs *before* the join).** A milestone-backed stream whose work has completed and landed on main should not linger as a `ready`/`merge_queue` row — that stale readiness is a **false** "ready to merge" nudge (the work is already on main; the stream is *closable, not mergeable*). Before the pure join, glob the stream files and for each with `stream.milestone: m-{id}` where the milestone's derived status (from the freshly-rolled `index.yml`) is `complete`, its stream `status` is **not** already `closed`, **and** it has no live worktree (`runtime.worktree` empty, or the path no longer resolves to a live `git worktree`) → **auto-close it**: set the stream file's `status: closed` and `merge.readiness: merged`, append a `merge.notes` line (*"auto-closed: milestone m-{id} complete + on main"*), refresh `updated_at`. Emit one line — *"auto-closed stale stream {id} — milestone m-{id} complete, already on main."* This is a legitimate source write (stream file = single-owner mutable; the milestone is done, so no live driver contends). **Guards:** a `deferred` linked milestone is **not** swept (its stream should pause, not close — Chief offers that interactively); a `complete` milestone **with a live worktree** is **not** swept here either — the forge-boot "Completed milestone + live worktree → finalize" path (SKILL Step 1) owns teardown, and closes the stream as part of it. Idempotent: an already-`closed` stream is skipped, so re-running the sweep is a no-op. No `streams/` dir, or nothing complete-and-open → the sweep is silent and the join runs unchanged.
265
- 1. Glob `streams/{stream}.yml`. Each → a row carrying its coordination state (`coordination` = the file's `status`), goal, runtime, ownership summary, `merge_readiness` (derived from the stream file's `merge.readiness`), `blocked_by`, `next_action` — all from the stream file.
266
- 2. For a stream with `stream.milestone: m-{id}`, read `phase`/activity from `milestone-{id}.yml` (`current.status`, `last_updated`) — **not** stored in the stream file. A linked milestone that is `deferred`/`complete`/absent → the row's `phase` reflects that.
267
- 3. Glob active milestones (from `index.yml`). Any active milestone with **no** stream file → emit a derived `stream: implicit` row (phase from the milestone, no coordination state) so coverage is guaranteed in the artifact — an active milestone can never be silently missing.
268
- 4. Derive `merge_queue:` — one entry per stream with `merge.readiness: ready`.
269
- 5. Write `active.yml` (`version`, `updated_at`, derived `streams:` sorted by id, derived `merge_queue:`). No hand-edited keys.
270
-
271
- Because each field has exactly one source and coverage is computed, the two-registry drift class (issue #4) cannot occur — this **supersedes** the advisory Registry Drift Check shipped in 0.33.0.
272
- **Format**: YAML for machine state, Markdown for human content.
273
- **`current.status` is authoritative.** Complete only at `current.status == complete`. 100% tasks ≠ done — still needs verifying + reviewing + a recorded human sign-off (`current.human_verified` — see Verification Gates → Human Verification Gate).
189
+ State lives in `.forge/`. One line per artifact; protocol detail lives in the named ADRs and skills:
190
+
191
+ - `project.yml` — vision, stack, design system, verification, constraints (<5 KB)
192
+ - `constitution.md` — active architectural gates
193
+ - `design-system.md` — component mapping table
194
+ - `requirements/m{N}.yml` — per-milestone structured requirements with `[NEEDS CLARIFICATION]` markers. **FR/DEF/NFR ids are globally unique across all milestone files** — reserve via the ID Reservation Protocol before writing. M9 e2e gate fields (`e2e`, `observable_outcome`, `validated`, hash) lazy-migrate; absent ⇒ `false`.
195
+ - `roadmap.yml` — phases, milestones, dependencies
196
+ - `state/index.yml`DERIVED milestone registry, **git-ignored render-on-read cache** (0.53.0): regenerated at every boot by Rollup 1.0 (`forge` SKILL Step 1.0) from the milestone files; never committed, never hand-edited, never written by worktree agents; absent on a fresh clone (the project sentinel is `project.yml`). Rows carry the derived `release_state`.
197
+ - **Derived `release_state`** ([ADR-024](../docs/decisions/ADR-024-merged-validated-fold.md)) — `{unmerged, merged, validated}` computed at rollup by `.claude/hooks/forge-release-fold.sh` (git-only, offline; the fold binary is the only source agent prose never re-derives it), never stored. Validation binding via `forge.validation_event`: `in_session_signoff` (default; absent this) or `promotion_approval` (CI-signed `promotion/{sha}` tag). Inert until a repo opts in; full spec in ADR-024 + the fold header.
198
+ - `state/milestone-{id}.yml`per-milestone cursor (**single source of truth**): position, decisions, blockers. One owner at a time.
199
+ - `state/desire-paths/` — append-only framework-usage observations, one file per observation, `scope: project|framework` (absent ⇒ project); occurrence counts derived by globbing. **`forge` boot is the single review owner** surfaces groups at 3+, persists declines (no re-nag), routes `framework`-scope signals upstream ([ADR-012](../docs/decisions/ADR-012-upstream-desire-path-feedback-channel.md)); `verifying` captures but never surfaces.
200
+ - `context.md` — active locked decisions + deferred ideas (≤12 KB); history archives to `context-archive.md`
201
+ - `streams/active.yml` — DERIVED Project Chief traffic map, git-ignored render-on-read cache see Stream Rollup below ([ADR-015](../docs/decisions/ADR-015-derived-stream-registry.md))
202
+ - `streams/{stream}.yml` (+ `brief.md`, `packages/{id}.yml`) per-stream **source of truth**: coordination lifecycle, ownership, shared surfaces, blockers, merge readiness, optional `stream.milestone` link (milestone-backed streams omit workflow phase the rollup reads it from the milestone)
203
+ - `research/milestone-{id}.md` — dated immutable findings snapshot
204
+ - `phases/milestone-{id}/{phase}-{name}/plan-{NN}.md` — task plans with must_haves frontmatter (`{id}`=milestone, `{phase}`=phase# verbatim, `{name}`=kebab, `{NN}`=seq)
205
+ - `refactor-backlog.yml` — refactoring catalog worked via quick-tasking; status vocab `pending | in_progress | done | dismissed | deferred`; terminal items auto-archive to `refactor-backlog-archive.yml` on the next reviewing/quick-tasking write
206
+ - `releases.yml` — append-only version-reservation registry (Version Reservation Protocol)
207
+ - `reservations.yml` — append-only id-reservation audit trail + cross-machine floor (ID Reservation Protocol)
208
+ - `archive/milestone-{id}/`archived milestone artifacts (archive-delete)
209
+ - `prototypes/{slug}.md` — resumable disposable-mockup state (`exploring | parked | graduated | discarded`); terminal `prototypes/archive/`. Single-owner, one driver. See `Skill(prototyping)`.
210
+
211
+ **Milestones** group phases into concurrent streams — own state file, no conflicts across sessions; can be deferred (frozen in place) or archive-deleted. Registry status vocab: `not_started | active | deferred | complete`. **`current.status` is authoritative** complete only at `current.status == complete`; 100% tasks ≠ done (verifying + reviewing + the recorded human sign-off still gate).
212
+
213
+ **Format:** YAML for machine state, Markdown for human content.
214
+
215
+ ### Stream Rollup
216
+
217
+ `active.yml` is a pure projection regenerated, never hand-edited — by a deterministic, idempotent **join**: per-stream files contribute coordination state; active milestones contribute `phase` (any active milestone with no stream file gets a derived `implicit` row — coverage is computed, not maintained); `merge_queue` derives from `merge.readiness: ready`. A **step-0 completed-stream auto-close sweep** runs first: a milestone-backed stream whose milestone is `complete`, not yet `closed`, with no live worktree is auto-closed — a done-and-merged milestone can never leave a zombie "ready to merge" row. Run by the main/orchestrator session at `forge` boot and `chief-of-staff` Show/Sync; worktrees never write it; milestone Rollup 1.0 runs **first** (the join reads the fresh `index.yml`). **Numbered procedure: the ADR-015 addendum.** Because each field has exactly one source and coverage is computed, two-registry drift cannot occur — supersedes the old advisory Registry Drift Check.
274
218
 
275
219
  ### State Commit Protocol
276
220
 
277
- `.forge/` durable state is **committed**, not just written to disk so project state survives machine loss and is pullable from any clone. At every phase **Handoff**, after writing state, the completing skill runs one scoped state-sync commit:
221
+ `.forge/` durable state is **committed**, not just written — project state survives machine loss and pulls from any clone. At every phase **Handoff**, after writing state, the completing skill runs one scoped state-sync commit:
278
222
 
279
223
  ```
280
224
  git add .forge/ # scoped — respects .gitignore; never `git add .`
281
225
  git commit -m "chore(forge): sync state after {phase} — m{N} {phase-name}"
282
226
  ```
283
227
 
284
- State-sync commits are separate from per-task code commits (which stay atomic during executing).
228
+ **In a worktree session, pin with the recorded path** — `git -C {worktree_path} add/commit` (`lifecycle.worktree_path` is authoritative over cwd; the process cwd can silently reset to the launch checkout across turns, and a wrong-cwd git op can even exit 0). Guard: compare `git rev-parse --show-toplevel` to the recorded path before the first write — full discipline in `executing` (Worktree cwd discipline) and `verifying` (Worktree cwd guard). State-sync commits are separate from per-task code commits (which stay atomic during executing).
285
229
 
286
230
  ### State Ownership (multi-worktree safety)
287
231
 
288
- `.forge/` is code-like: it branches with the repo, syncs via git, and commits
289
- with the work it describes. Every artifact has a sharing class:
232
+ `.forge/` is code-like: it branches with the repo, syncs via git, and commits with the work it describes. Every artifact has a sharing class (rationale: [ADR-011](../docs/decisions/ADR-011-shared-state-taxonomy.md) + addendum):
290
233
 
291
234
  | Class | Files | Write rule |
292
235
  |---|---|---|
293
- | **Single-owner mutable** | `state/milestone-{id}.yml`, `phases/milestone-{id}/`, `research/milestone-{id}.md` | Exactly one writer at a time — the worktree (or main session) currently driving that milestone. **Other worktrees never touch it**, not even to read-then-write — they pull from main if they need it. |
294
- | **Single-owner mutable (per stream)** | `streams/{stream}.yml`, `streams/{stream}/brief.md`, `streams/{stream}/packages/*.yml` | The stream's **current driver** writes them one writer at a time, like the milestone file. Distinct streams own distinct files (no cross-stream contention). `active.yml` is **derived** from them: a worktree driving a stream may write its own stream file but **never** `active.yml` or a peer stream's file. |
295
- | **Derived (git-ignored render-on-read cache)** | `state/index.yml`, `streams/active.yml`, the **`release_state`** column rendered into `index.yml` (ADR-024) | Never hand-edited, **never committed** — git-ignored local caches (0.53.0), regenerated by rollup from their sources (`index.yml` ← `milestone-*.yml` via Rollup 1.0; `active.yml` ← per-stream files + active milestones via the Stream Rollup; `release_state` ← `forge-release-fold.sh` over git truth) at every boot. Because the value is computed on read and never stored in git, a committed projection cannot drift stale from its source, and **any** session (not just the orchestrator) may render them locally without a `main` write. Absent on a fresh clone until the first boot; readers must render-then-read and must **not** use their absence as a "not a Forge project" sentinel (that is `project.yml`). |
296
- | **Append-only shared** | `releases.yml` (version reservations), `reservations.yml` (ADR/DEF/FR/NFR id **audit trail + cross-machine floor**), `state/desire-paths/*` (one file per observation) | Many writers OK; structure prevents collision. `releases.yml` uses the Version Reservation Protocol (append + commit + push). `reservations.yml` is written by `forge-reserve` (ADR-021) and committed with the caller's work — it is no longer the id *coordination* point (the machine-local ledger is), just the durable committed shadow. Desire-paths use distinct filenames so two writers never touch the same file. |
297
- | **Machine-local runtime** | `.git/forge/id-reservations` (id-reservation ledger), `.git/forge/integration-pending` (integration flag) | In the git common dir, shared across a repo's worktrees on one machine, **not** git-tracked. `forge-reserve` writes the ledger under a `mkdir` lock; excluded from framework context. Machine-local by design (cross-machine falls back to the `main:reservations.yml` floor). |
298
- | **Shared mutable** | `context.md`, `refactor-backlog.yml`, `requirements/m{N}.yml` (ID space `FR-`/`DEF-`/`NFR-` is globally shared even though each milestone owns its file) | Write only to your milestone/stream block. Within a block, append-only; strike through instead of rewriting. |
299
- | **Stable shared** | `project.yml`, `constitution.md`, `design-system.md`, `roadmap.yml`, `FORGE.md`, `.claude/skills/`, `.claude/agents/` | Rarely changes except via `/upgrading`. Canonical on main; worktrees lag until they rebase. An upgrade run touches **only the checkout it runs in**, so after upgrading in main, live worktrees keep their branch-point framework files — `upgrading` **Step 8** enumerates live `forge/m-*` worktrees and offers to rebase each (never auto), and the opt-in `forge.worktree_rebase_check` watches these files as a boot-time safety net. Gitignored carve-outs (`.mcp.json`, experimental hooks) don't rebase — re-run the experimental installer if the upgrade changed them. |
300
-
301
- Cross-tree edits land on the current branch and are invisible to other worktrees
302
- until they pull/rebase. `forge` surfaces live worktrees at boot **and enforces
303
- the Live-Worktree Ownership Gate** (forge SKILL 1.1a): when invoked from main for
304
- a milestone that has a live worktree, milestone-owned writes are **blocked**
305
- until the operator enters the worktree, syncs it, retires it, or records an
306
- explicit `lifecycle.ownership_override`. `discussing` and `reviewing` warn before
307
- touching shared-mutable blocks; optional `forge.worktree_rebase_check` detects
308
- stale shared files inside worktrees. See
309
- [ADR-011](../docs/decisions/ADR-011-shared-state-taxonomy.md) for rationale.
310
-
311
- ### State Ownership rules (quick reference)
312
-
313
- - `state/milestone-{id}.yml` is the **single source of truth**; exactly one agent owns it at a time. **Other worktrees never write it** — and from a main checkout this is **enforced**, not just advised: the Live-Worktree Ownership Gate (forge SKILL 1.1a) blocks milestone-owned writes from main while a live worktree owns the milestone, with a recorded `lifecycle.ownership_override` as the only (never-silent) bypass.
314
- - Worktree / parallel agents write **only** their own milestone file and **append** desire-path files. They **never** write `index.yml`.
315
- - `index.yml` is **regenerated by rollup** (read every `milestone-*.yml` → rewrite the registry) by the main/orchestrator session — on `forge` resume and at `orchestrating` teardown. The rollup is deterministic + idempotent, so it **is** the reconcile step — never a hand-merge.
316
- - For shared-mutable files: write only your milestone's block; within a block, append-only (strike through, don't rewrite).
236
+ | **Single-owner mutable** | `state/milestone-{id}.yml`, `phases/milestone-{id}/`, `research/milestone-{id}.md` | Exactly one writer at a time — the checkout currently driving that milestone. Other worktrees never touch it (not even read-then-write; pull from main instead). |
237
+ | **Single-owner mutable (per stream)** | `streams/{stream}.yml`, `streams/{stream}/brief.md`, `streams/{stream}/packages/*.yml` | The stream's current driver writes them; distinct streams own distinct files no cross-stream contention. |
238
+ | **Derived (git-ignored render-on-read cache)** | `state/index.yml`, `streams/active.yml`, the rendered `release_state` column | Never hand-edited, **never committed** — regenerated by rollup from sources at every boot. Any session may render its own copy; absence on a fresh clone is normal (render-then-read; the sentinel is `project.yml`). |
239
+ | **Append-only shared** | `releases.yml`, `reservations.yml`, `state/desire-paths/*`, `deferred-issues/*` | Many writers OK; structure prevents collision (append-only entries / distinct filenames). |
240
+ | **Machine-local runtime** | `.git/forge/id-reservations`, `.git/forge/integration-pending` | Git common dir shared across a machine's worktrees, never git-tracked, excluded from framework context. |
241
+ | **Shared mutable** | `context.md`, `refactor-backlog.yml`, `requirements/m{N}.yml` | Write only your milestone/stream block; within a block, append-only strike through instead of rewriting. |
242
+ | **Stable shared** | `project.yml`, `constitution.md`, `design-system.md`, `roadmap.yml`, `FORGE.md`, `.claude/skills/`, `.claude/agents/` | Rarely changes except via `/upgrading`. Canonical on main; worktrees lag until they rebase. An upgrade touches only its own checkout — `upgrading` Step 8 offers live-worktree rebases (never auto); opt-in `forge.worktree_rebase_check` is the boot-time net. Gitignored carve-outs (`.mcp.json`, experimental hooks) propagate via the experimental installer, not git. |
243
+
244
+ Ownership rules (quick reference):
245
+
246
+ - The milestone cursor has exactly one owner. From a main checkout this is **enforced**: the Live-Worktree Ownership Gate (`forge` SKILL 1.1a) blocks milestone-owned writes from main while a live worktree owns the milestone; a recorded `lifecycle.ownership_override` is the only — never-silent — bypass.
247
+ - Worktree/parallel agents write only their own milestone file and append desire-path files; they never write `index.yml` or `active.yml` — the rollup regenerates both and **is** the reconcile step, never a hand-merge.
248
+ - Cross-tree edits land on the current branch and are invisible to other worktrees until they pull/rebase; `forge` surfaces live worktrees at boot, and `discussing`/`reviewing` warn before touching shared-mutable blocks.
317
249
  - Same-milestone parallel work needs Chief/Streams contracts; M10 file claims are optional defense-in-depth.
318
250
 
319
251
  ### Version Reservation Protocol
320
252
 
321
- The project version + CHANGELOG slot are **shared resources**when two milestones run in parallel sessions, each independently bumping `package.json`/version and adding a CHANGELOG entry collides. `.forge/releases.yml` removes the number from per-milestone scope.
322
-
323
- - **Reserve early, at planning.** When a milestone's delivery will bump the version, the `planning` skill appends ONE entry to `.forge/releases.yml`: `{milestone, version, bump, reserved_at, summary}`. The reserved version = highest `version:` in the file bumped by the change's semver level (capability → minor; fix/doc → patch — see version-bump criteria below).
324
- - **Append-only — never edit prior entries.** Distinct lines = no cross-session conflict. Commit + push the reservation BEFORE editing the version file, so a parallel session pulls and sees the number taken.
325
- - **Delivery never invents a number.** The `executing`/delivery step writes only the version reserved in `releases.yml`, then its CHANGELOG entry. If no reservation exists (milestone planned before this file), it reserves at delivery: pull, take `max() + bump`, append, push, then write.
326
- - **Race resolution.** Two near-simultaneous reservations: the second push rebases onto the first (append-only → clean), re-reads `max()`, takes the next number.
327
-
328
- **Version-bump criteria (0.x projects):** patch (`0.x.y`) = bug fix, doc tweak, sync-only, refinement. Minor (`0.x.0`) = new skill/agent/routing-row/auto-trigger/state-artifact/capability. Major (`x.0.0`) = post-1.0 breaking change. Default to minor when in doubt — a capability addition shipped as a patch drifts the version away from truth.
253
+ The project version + CHANGELOG slot are shared resources — `.forge/releases.yml` (append-only) removes the number from per-milestone scope. **Reserve early, at planning**: append ONE `{milestone, version, bump, reserved_at, summary}` entry — the highest reserved version bumped by this change's semver level — and **commit + push the reservation BEFORE editing the version file** so a parallel session sees the number taken. Never edit prior entries. **Delivery never invents a number** — write only the reserved version; no reservation → reserve at delivery (pull, `max() + bump`, append, push, then write). **Race:** the second push rebases onto the first (append-only → clean), re-reads `max()`, takes the next number. **Bump criteria (0.x):** patch = bug fix/doc/sync/refinement; minor = new skill/agent/routing-row/auto-trigger/state-artifact/capability; major = post-1.0 breaking. Default to minor when in doubt — a capability shipped as a patch drifts the version away from truth.
329
254
 
330
255
  ### ID Reservation Protocol
331
256
 
332
- Sequential IDs — `ADR-NNN` (decision records), `DEF-NNN` (deferred requirements), `FR-NNN`/`NFR-NNN` (requirements), plus milestone ids (`M-NN`) and refactor-backlog ids (`R0NN`) — are **shared cross-worktree resources** the same way the version number is. "Scan the tree for the highest and increment" only sees *committed* state, so two worktrees branched from the same baseline each claim the same next number and collide **silently until merge** a costly multi-file renumber pass. [ADR-016](../docs/decisions/ADR-016-id-reservation-protocol.md) tried to fix this by reserving in the branch-tracked `reservations.yml` and *committing + pushing before allocating*, but a reservation on an unmerged branch is invisible to every sibling worktree (they share `.git`, not branches) — so collisions continued. [ADR-021](../docs/decisions/ADR-021-id-reservation-ledger-git-common-dir.md) (**supersedes ADR-016**) moves the coordination point to a **machine-local ledger in the git common dir**, the same primitive as the integration flag: every sibling worktree of the repo on a machine sees it the moment it is written no push, no pull, no merge.
333
-
334
- - **Reserve with the helper.** `forge-reserve <kind> --milestone <id> [--summary <text>]` (`.claude/hooks/forge-reserve.sh`, `kind ∈ {adr, def, fr, nfr, milestone, refactor}`) allocates the next number under a portable `mkdir` lock and prints it on stdout. Id formats differ per kind (ADR-023): `adr/def/fr/nfr` → `PREFIX-0NN` (hyphen, zero-padded); `milestone` → `M-NN` (hyphen, **unpadded** — ids are used bare, `milestone-26.yml`/`m-26`); `refactor` → `R0NN` (**no hyphen**, zero-padded — matches the backlog convention). It **dual-writes**: one TSV line to `.git/forge/id-reservations` (the machine-local coordination ledger) and one `{kind, id, milestone, reserved_at, summary}` block to the tracked `.forge/reservations.yml`. Neither is committed by the helper — **the caller commits `reservations.yml` with its work** (unchanged handoff). Reserve *before* creating the artifact; the printed id is the one to use.
335
- - **Next number = `max(ledger ∪ in-tree ∪ main:reservations.yml) + 1` per kind.** Three inputs, unioned: the **ledger** (same-machine siblings, *including uncommitted* — the input ADR-016 could not see), the **in-tree** landed ids, and **`main:reservations.yml`** (the cross-machine / cold-start floor). The in-tree scan is per-kind: `adr` → `docs/decisions/ADR-NNN*`; `fr/nfr/def` → `.forge/requirements/*.yml`; `milestone` → live `state/milestone-*.yml` **∪ archived** `.forge/archive/milestone-*/`; `refactor` → `refactor-backlog.yml` **∪** `refactor-backlog-archive.yml` (ADR-023 — reading archives means a compacted/archived id is never reallocated). Taking all three inputs keeps it backward-compatible (landed & pre-ledger IDs respected) while giving the ledger authority for the same-machine hot path.
336
- - **`reservations.yml` is now the durable audit trail + cross-machine floor**, not the coordination mechanism. Append-only, distinct lines → it unions cleanly at merge (it stops being a merge-conflict source). An optional `forge-reserve --verify` can diff ledger vs. `reservations.yml`.
337
- - **Reserve points.** `architecting` reserves an `adr` before authoring it; `planning` reserves `fr`/`nfr`/`def` before writing them into requirements; the three milestone-id sites (`forge` Rollup, `discussing`, `prototyping`) reserve `milestone`; `reviewing` reserves `refactor` for a new backlog id — all via `forge-reserve` (ADR-023).
338
- - **DI-NNN eliminated, not reserved (ADR-023).** `executing`'s deferred-*issue* ids used to be a scan-and-increment `DI-NNN` counter in a single `deferred-issues.md` — the same cross-worktree collision in kind. Rather than reserve them, deferred issues are now **counter-free per-issue files** under `.forge/deferred-issues/` (`{date}-{milestone}-{slug}.md`, the desire-path pattern): distinct filenames, no shared counter, nothing to collide on. Nothing cross-references a deferred issue by number, so the id density reservation buys is worthless here. Legacy `deferred-issues.md` is still read until migrated.
339
- - **Cross-machine gap (accepted).** The ledger is machine-local (like `integration-pending`). Two *concurrent* worktrees on *different* laptops, both reserving before either merges, still fall back to merge-time renumber — bounded by the `main:reservations.yml` floor, so no worse than ADR-016.
340
- - **Lazy migration.** No ledger ⇒ the helper creates it on first reserve, seeded by the `max()` (which reads in-tree + `main:reservations.yml`). Existing `reservations.yml` is untouched and still respected. Projects that never run concurrent worktrees see no behavior change.
341
-
342
- This is the cross-worktree mechanism the FR/DEF/NFR "globally unique" rule (State Management, above) previously lacked, now made collision-safe for same-machine worktrees. Versions stay in `releases.yml` (separate semver/CHANGELOG semantics); everything else reserves through `forge-reserve`.
257
+ Sequential ids — `ADR-NNN`, `DEF-NNN`, `FR-`/`NFR-NNN`, milestone `M-NN`, refactor `R0NN` — are shared cross-worktree resources; scan-and-increment sees only committed state, so concurrent worktrees claim the same number and collide silently until merge. **Reserve with the helper before creating the artifact**: `forge-reserve <kind> --milestone <id> [--summary <text>]` (`.claude/hooks/forge-reserve.sh`, kinds `adr|def|fr|nfr|milestone|refactor`; id formats per [ADR-023](../docs/decisions/ADR-023-extend-id-reservation-to-milestone-refactor-ids.md)) prints the id and dual-writes the **machine-local ledger** (`.git/forge/id-reservations`, git common dir — the same-machine coordination point, written under a `mkdir` lock) plus the tracked `.forge/reservations.yml` (durable audit trail + cross-machine floor; the caller commits it with the work). **Next number = `max(ledger in-tree main:reservations.yml) + 1`** per kindthe in-tree scan includes archives, so a compacted id is never reallocated. Reserve points: `architecting` (adr), `planning` (fr/nfr/def), `forge` Rollup / `discussing` / `prototyping` (milestone), `reviewing` (refactor). Deferred issues are counter-free per-issue files (`.forge/deferred-issues/{date}-{milestone}-{slug}.md`) no DI-NNN, nothing to collide on. Accepted gap: concurrent reserves on *different machines* fall back to merge-time renumber, bounded by the floor. On any residual collision (legacy/un-reserved ids), **keep the older milestone's id and renumber the newer**. Lazy migration — the ledger self-creates on first reserve. Full rationale: [ADR-021](../docs/decisions/ADR-021-id-reservation-ledger-git-common-dir.md) (supersedes ADR-016) + ADR-023. Versions stay in `releases.yml` (separate semver/CHANGELOG semantics).
343
258
 
344
259
  ## Deviation Rules
345
260
 
@@ -369,34 +284,22 @@ verification:
369
284
  max_retries: 2 # max attempts per command
370
285
  ```
371
286
 
372
- - Auto-detected from `package.json` scripts during init
373
- - Advisory mode: pre-existing failures warn, don't block
374
- - Auto-fix loop: read output → fix → amend → re-run (up to max_retries)
375
- - 3-strike: retries count toward task limit
376
- - Empty commands = no gate (opt-out)
377
- - `verification.e2e_soft_cap` (default 10) — advisory cap on `e2e:true` stories per milestone surfaced by the `reviewing` skill. Soft — never blocks.
287
+ Auto-detected from `package.json` at init. Advisory mode warns, never blocks. Auto-fix loop: read output → fix → amend → re-run (≤ `max_retries`; retries count toward the 3-strike task limit). Empty commands = no gate (opt-out). `verification.e2e_soft_cap` (default 10) — advisory per-milestone cap on `e2e:true` stories, surfaced by `reviewing`, never blocks.
378
288
 
379
289
  ### Truth checks (executable must_haves)
380
290
 
381
- Plans MAY declare a per-truth `check:` shell command in `must_haves.truths` (exit 0 = pass; authored goal-backward at planning, before code exists; linted by planning Step 8 dimension 10). `executing` runs declared checks once at plan completion (early fix-loop signal, never a verdict); `verifying` re-executes them and applies the **executed-evidence rule**: VERIFIED only with a recorded command + exit code + output excerpt — no executed evidence → UNCERTAIN or human-route, never VERIFIED. Legacy bare-string truths stay valid; details live in the planning/executing/verifying skills + plan template.
291
+ Plans MAY declare a per-truth `check:` shell command in `must_haves.truths` (exit 0 = pass; authored goal-backward at planning, before code exists; linted by planning Step 8). `executing` runs declared checks once at plan completion (early fix-loop signal, never a verdict); `verifying` re-executes them and applies the **executed-evidence rule**: VERIFIED only with a recorded command + exit code + output excerpt — no executed evidence → UNCERTAIN or human-route, never VERIFIED. Legacy bare-string truths stay valid.
382
292
 
383
293
  ### Human Verification Gate (close precondition)
384
294
 
385
- **No milestone reaches `complete` and no orchestration worktree is torn down until a human has explicitly signed off** — recorded as `current.human_verified` in `milestone-{id}.yml`. Code-level verification (tests, lint, type-check, the 3-level goal-backward pass) is necessary but **never sufficient** to close a milestone. A human must confirm the work in whatever form fits the milestone — device session, visual check, e2e walk, smoke test ("of some sorts").
386
-
387
- - **Captured** by `verifying` — after the verdict it prompts for explicit sign-off and writes `current.human_verified: {at, method, notes}`. If the human can't verify yet (e.g. a device session is still pending), the field stays unset and downstream close blocks.
388
- - **Enforced (hard block)** at both close points — `reviewing` refuses to set `current.status: complete`, and `orchestrating` Step 5 refuses teardown of **un-merged** work (see the teardown split below), unless `current.human_verified` is present.
389
- - **Recorded override** is the only bypass. A human may explicitly close without verifying, but the skill writes `human_verified: {method: override, override: true, reason}` and surfaces it in the report. Silent close is impossible — an agent must **never** present milestone close / orchestration teardown as a peer alternative to "do the verification". The verification is a precondition, not an option.
390
-
391
- Lazy migration: milestones with no `human_verified` field (pre-0.27.0) simply hit the prompt on their next close transition; already-`complete` milestones are never re-gated.
392
-
393
- **Per-binding relocation (B5, operator-ratified 2026-07-15).** The gate above is the **`in_session_signoff`** binding — the default (`forge.validation_event` absent ⇒ this), preserved exactly. A repo that opts into **`validation_event: promotion_approval`** relocates the human gate to the **deploy boundary**: `complete` becomes the machine/code-verified state (`reviewing` closes without in-session sign-off, its health report noting *"human gate: at promotion (B5)"*), and the derived `release_state: validated` — computed by `forge-release-fold.sh` when a CI-signed promotion tag covers the unit — **is** the human gate (the full release_state ladder lands in State Management with phase 52). The mode is **not agent-flippable**: `validation_event`/`project.yml` sits on the step-2 **protected-paths** hold (F4/F5), so a diff flipping it blocks auto-merge for operator review.
394
-
395
- **Teardown splits on merged-ness (operator ruling 2026-07-16 — binding-independent).** Worktree teardown auto-proceeds when the unit renders `merged`/`validated` via `forge-release-fold.sh` — the work is on main; removing the worktree loses nothing. The human gate applies only to **un-merged** teardown. The close gate is unaffected by this split — it keeps the per-binding rule above.
396
-
397
- **Grandfather (legacy-complete marker).** A milestone already stored `current.status: complete` with **no** `signoff/<wu-id>` tag (the pre-M0 history — 26 milestones as of 0.53.0) is **grandfathered**: readers honor it as complete and it is **never re-gated** by any later sign-off convention. "Complete with no signoff tag" **is** the legacy-complete marker — no field is stamped onto those files. Only *new* completions going forward can be asked to carry a signature (that forward-gate is autonomous-lane and is **not** wired in the attended lane — see below).
295
+ **No milestone reaches `complete` and no un-merged orchestration worktree is torn down until a human has explicitly signed off** — recorded as `current.human_verified` in `milestone-{id}.yml`. Code-level verification (tests, lint, the 3-level goal-backward pass) is necessary but **never sufficient**.
398
296
 
399
- **Where the Contract M0 sign-off convention is adopted, `human_verified` is a derived echo, not the source of truth.** The authoritative predicate becomes `verify-signoff <wu-id>` exiting 0 an SSH-signed `signoff/<wu-id>` tag an agent cannot forge (see `docs/sign-off-convention.md` and `.claude/hooks/{verify-signoff,wu-done}.sh`). `current.human_verified` may still be written for human-readable display, but treat it as a cache of the signature check, never the gate itself. Core does not yet wire this predicate into `verifying`/`reviewing`'s own hard-block logic automatically — that remains a flagged follow-up (Contract M0 §D2 scoped to a minimal fold stub, not the full skill wiring).
297
+ - **Captured** by `verifying` — prompts for explicit sign-off after the verdict; if the human can't verify yet, the field stays unset and downstream close blocks.
298
+ - **Enforced (hard block)** at both close points — `reviewing` refuses `current.status: complete`, and `orchestrating` refuses teardown of un-merged work — unless the field is present.
299
+ - **Recorded override** is the only bypass (`human_verified: {method: override, override: true, reason}`), echoed in the report. Silent close is impossible; an agent must never present override as a peer alternative to doing the verification.
300
+ - **Teardown splits on merged-ness** (binding-independent): teardown auto-proceeds when the unit renders `merged`/`validated` via the fold — the work is on main, removal loses nothing; the human gate applies to **un-merged** teardown only. The close gate is unaffected by this split.
301
+ - **Bindings ([ADR-024](../docs/decisions/ADR-024-merged-validated-fold.md)):** default `in_session_signoff` preserves this gate exactly. `promotion_approval` relocates the human gate to the deploy boundary — `complete` becomes the machine-verified state and the derived `validated` (CI-signed promotion tag, verified offline) **is** the human gate. The mode is not agent-flippable (`validation_event` sits on protected paths).
302
+ - **Migration:** lazy — pre-gate milestones simply hit the prompt on their next close; milestones already `complete` with no `signoff/<wu-id>` tag are grandfathered, never re-gated. Where the M0 sign-off convention is adopted (`docs/sign-off-convention.md`), the authoritative predicate is `verify-signoff <wu-id>` exiting 0 — an SSH-signed tag an agent cannot forge; `current.human_verified` is then a derived echo, never the gate itself.
400
303
 
401
304
  ## Beads Integration (Optional)
402
305
 
@@ -404,7 +307,4 @@ With Beads installed: `bd prime` (session context), `bd ready` (unblocked tasks)
404
307
 
405
308
  ## Atomic Commits
406
309
 
407
- One commit per task. Format: `{type}({scope}): {description}`
408
- Types: `feat`, `fix`, `test`, `refactor`, `chore`, `docs`
409
- Never `git add .` or `git add -A` — stage individually.
410
- Phase handoffs additionally emit one scoped `chore(forge): sync state …` commit (see State Commit Protocol) so `.forge/` state never drifts out of git.
310
+ One commit per task. Format: `{type}({scope}): {description}` — types `feat fix test refactor chore docs`. Never `git add .` or `git add -A` — stage individually. Phase handoffs additionally emit one scoped `chore(forge): sync state …` commit (State Commit Protocol) so `.forge/` state never drifts out of git.