forge-orkes 0.59.1 → 0.61.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.61.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.)
@@ -204,6 +204,8 @@ models:
204
204
 
205
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.
206
206
 
207
+ **Spawn gate — predicate A (LOCKED, context.md § M24; refined 2026-07-20/m-33).** Executing's *mandatory* subagent spawn on a complexity-tagged task fires **only when the task's resolved model differs from the live session model** (the model the session is actually running on). Resolved == live — always the case when there is **no `models:` block** (the chain falls to parent == live) — makes the spawn a pure no-op (briefing overhead, no routing change), so the tag becomes a **context signal only**: spawn iff a context trigger (20+ files / deep subsystem / >40%) independently fires. The inline-task limitation above is real, but the leak it names only bites when a *different* model was actually available — exactly the resolved-≠-live case the gate keeps mandatory. (WHY: an unconditional spawn burned ~315k subagent tokens on m-30 for zero routing benefit.)
208
+
207
209
  ## Agents
208
210
 
209
211
  | Agent | Role | Tools | When |
@@ -281,6 +283,8 @@ git add .forge/ # scoped — respects .gitignore; never `git add .`
281
283
  git commit -m "chore(forge): sync state after {phase} — m{N} {phase-name}"
282
284
  ```
283
285
 
286
+ **In a worktree session, pin these with the recorded path** — `git -C {worktree_path} add .forge/` then `git -C {worktree_path} commit …` (`{worktree_path}` = `lifecycle.worktree_path`, authoritative over cwd). The process cwd can silently reset to the launch (main) checkout across a turn boundary; a bare `git add .forge/…` then hits the wrong checkout (worktree-only content fails to stage) and a bare `git push origin HEAD:main` can even **exit 0** against a stale local `main` HEAD — a silent wrong-tree commit/push. Cheap guard before the first write: compare `git rev-parse --show-toplevel` to the recorded path; mismatch → use `-C` (or `cd` back).
287
+
284
288
  State-sync commits are separate from per-task code commits (which stay atomic during executing).
285
289
 
286
290
  ### State Ownership (multi-worktree safety)