forge-orkes 0.26.0 → 0.29.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.26.0",
3
+ "version": "0.29.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"
@@ -42,13 +42,23 @@ Never accumulate decisions in working memory. Never batch writes to convergence.
42
42
 
43
43
  ### Write Protocol
44
44
 
45
+ `context.md` is **shared mutable** state per FORGE.md State Ownership — writes follow two structural rules:
46
+ - **Scope to your milestone's block.** Different milestone blocks → git merges cleanly across worktrees.
47
+ - **Append-only within a block.** When superseding an older decision, *strike through* (`~~old decision~~`) and append the new line below — never rewrite in place. Same-line concurrent edits become impossible by construction; reading the history of a decision becomes possible by inspection.
48
+
45
49
  **On first decision of the session:**
46
50
  - Check if `.forge/context.md` exists. If not → create it from `.forge/templates/context.md`
47
51
  - Add a milestone heading inside `## Locked Decisions`: `### M{id} — {name} (drafting)` (use "M?" if no milestone yet)
52
+ - **Cross-tree liveness check (informational).** If editing an existing block for milestone `{id}`, run `git worktree list --porcelain | awk '/^branch / && $2 ~ /refs\/heads\/forge\/m-'"$id"'(-|$)/ {print}'`. Any hit means m{id} is **live in another worktree** — surface one line: *"m{id} is live in worktree at `{path}`. Edits land cleanly on main but won't be visible to that worktree until it rebases or pulls."* Proceed with the write — main is often the canonical author of `context.md`, this is a heads-up, not a block.
48
53
 
49
54
  **On each confirmed decision:**
50
55
  - Append under the milestone heading: `- **[Topic]**: [Decision]. Reason: [Why]`
51
56
 
57
+ **On superseding a prior decision (in your own milestone's block):**
58
+ - Find the original line. Wrap it in `~~ ~~` (markdown strikethrough). DO NOT delete it.
59
+ - Append the new decision on a new line below it: `- **[Topic]**: [Decision]. Reason: [Why]. Supersedes ~~prior on {date}~~.`
60
+ - Why: shared-mutable append-only rule (FORGE.md State Ownership). Decisions stay readable as history; concurrent writes from another worktree on a different bullet can't conflict with this one.
61
+
52
62
  **On deferred ideas** (user says "not now" / "later" / "skip"):
53
63
  - Append to `## Deferred Ideas` immediately: `- [Idea] — Deferred because: [reason]`
54
64
 
@@ -9,6 +9,33 @@ Entry point. Detect tier, route skills, manage transitions. New projects → ini
9
9
 
10
10
  ## Step 1: Read State
11
11
 
12
+ **Preflight (multi-tree visibility) — run before everything in this section.**
13
+
14
+ The presence of live Forge worktrees changes the meaning of "which milestone am I editing" — the operator needs to see the picture before any state edit. Cheap to compute, free if there's nothing to show.
15
+
16
+ ```bash
17
+ # This session's checkout — main or a worktree? --git-dir == --git-common-dir → main; differ → linked worktree.
18
+ this_dir=$(git rev-parse --git-dir 2>/dev/null) || exit 0
19
+ common_dir=$(git rev-parse --git-common-dir)
20
+ in_main_checkout=$([ "$this_dir" = "$common_dir" ] && echo true || echo false)
21
+
22
+ # Forge worktrees = branch matches forge/m-*
23
+ git worktree list --porcelain 2>/dev/null \
24
+ | awk '
25
+ /^worktree / { path=$2 }
26
+ /^branch / { br=$2; if (br ~ /refs\/heads\/forge\/m-/) print path " " br }
27
+ '
28
+ ```
29
+
30
+ **If `in_main_checkout: true`** AND any Forge worktrees are reported:
31
+ - Surface them at the top of the boot output, one line each: *"m{id} is live in worktree `{path}` on branch `{branch}`. Edits to its milestone state belong there; shared-doc edits here (`context.md`, `refactor-backlog.yml`) won't be visible to it until it rebases or pulls."*
32
+ - This is informational — `forge` continues to Rollup + selection. The operator now knows. (See FORGE.md State Ownership for the full taxonomy.)
33
+
34
+ **If `in_main_checkout: false`** (this session is itself a worktree) AND `forge.worktree_rebase_check: true` in `.forge/project.yml` (default `false` — opt-in):
35
+ - Diff `.forge/` between this worktree's `HEAD` and `origin/main` (or `main` if no remote), restricted to the **shared-mutable** + **stable-shared** files per the FORGE.md taxonomy (`context.md`, `refactor-backlog.yml`, `project.yml`, `constitution.md`, `design-system.md`, `roadmap.yml`, `requirements/m{N}.yml`).
36
+ - Any differ → surface: *"`{file}` has changed on main since this worktree branched. Pull just this file (`git restore --source main -- {file}`), rebase, or skip?"* — operator decides.
37
+ - Setting absent or `false` → no-op.
38
+
12
39
  ### 1.0 State Rollup (index.yml is derived)
13
40
 
14
41
  `index.yml` is a **derived registry** — regenerate it from the milestone files before reading, and after any milestone CRUD (promote/defer/resume/delete). **Never hand-edit `index.yml`.**
@@ -355,7 +355,38 @@ User selects per stack.
355
355
  - `.forge/state/index.yml` — regenerate via the `forge` **Rollup** from the milestone file (do not hand-write the registry). It will derive `status: not_started` for milestone 1.
356
356
  - `.forge/state/desire-paths/` — create the (empty) directory for append-only observations.
357
357
  6. Templates as needed
358
- 7. **State-sync commit**: `git add .forge/` then `git commit -m "chore(forge): initialize forge state"` (scoped; never `git add .`) — so the new project's state is in git from the start.
358
+ 7. **Track Forge product files in `.gitignore`.** Forge installs into `.claude/` (skills, agents, hooks, `settings.json`) these are *product files*, not per-machine config, and **must be tracked in git** for two reasons: (a) `/forge` commands only exist in checkouts that have `.claude/skills/`, so a git worktree with `.claude/` fully ignored has no `/forge`; (b) M10 worktrees inherit hooks via tracked files (`.claude/hooks/*.sh`) an ignored `.claude/` silently disables them (see ADR-007 + ADR-010 "related concerns").
359
+
360
+ Detect the failure mode + apply the carve-out:
361
+
362
+ ```bash
363
+ if grep -qE '^[[:space:]]*\.claude/?[[:space:]]*$' .gitignore 2>/dev/null && \
364
+ ! grep -qE '^![[:space:]]*\.claude/(skills|agents|hooks)' .gitignore; then
365
+ echo "[Forge] .gitignore ignores .claude/ wholesale — Forge worktrees won't have /forge."
366
+ fi
367
+ ```
368
+
369
+ Then **replace** any bare `.claude/` rule (or **append** if no `.gitignore` exists) with the recommended carve-out:
370
+
371
+ ```gitignore
372
+ # Claude Code — track Forge product files, ignore per-machine + scratch
373
+ .claude/*
374
+ .claude/.DS_Store
375
+ !.claude/settings.json
376
+ !.claude/skills/
377
+ !.claude/agents/
378
+ !.claude/hooks/
379
+ !.claude/commands/
380
+ .claude/settings.local.json
381
+ .claude/projects/
382
+ .claude/worktrees/
383
+ ```
384
+
385
+ `settings.local.json` (per-machine permission allowlists), `projects/` (Claude Code conversation transcripts), and `worktrees/` (app-managed scratch) **must stay ignored** — they're private/per-machine, not product.
386
+
387
+ **Confirm with the user first** in brownfield (this edits a user-owned file). In greenfield with no existing `.gitignore`, write it directly. Re-run is idempotent — the carve-out lines `grep` to themselves.
388
+
389
+ 8. **State-sync commit**: `git add .gitignore .claude/settings.json .claude/skills/ .claude/agents/ .claude/hooks/ .forge/` then `git commit -m "chore(forge): initialize forge state + track product files"` (scoped; never `git add .`) — so the new project's state and Forge product files are both in git from the start.
359
390
 
360
391
  *"Initialized. Ready?"*
361
392
 
@@ -79,6 +79,7 @@ Tag every finding:
79
79
 
80
80
  For independent topics, research simultaneously:
81
81
  - Spawn separate research agents per topic with focused scope
82
+ - **Pass `model` param** from `project.yml` model routing (`models.skills.researching` → `models.default` → parent). Display: *"Spawning researchers with model: {model} (from {source})"*. Subagents don't inherit skill routing — without the param they run on the parent (often opus), defeating the cost intent.
82
83
  - Collect and synthesize results
83
84
 
84
85
  Never research sequentially when topics are independent.
@@ -312,6 +312,12 @@ Deferred issues triage: If `.forge/deferred-issues.md` has `status: pending` ite
312
312
 
313
313
  ### Backlog
314
314
 
315
+ `refactor-backlog.yml` is **shared mutable** state per FORGE.md State Ownership — many milestones append over time. Two rules apply:
316
+ - **Items carry `milestone:` scoping** (already enforced by the schema below) — concurrent appends from different milestones touch different items, so git merges cleanly.
317
+ - **Existing items are append-only** in spirit: change `status:` and add `completed:`/`deferred_at:` rather than rewriting the item's body. Compaction-on-write (below) handles physical relocation of terminal items.
318
+
319
+ **Cross-tree liveness check (informational).** Before writing, run `git worktree list --porcelain | awk '/^branch / && $2 ~ /refs\/heads\/forge\/m-/ {print}'`. Any non-empty output means another milestone is live in a worktree — surface one line: *"{N} other Forge milestone(s) live in worktrees ({list}). Backlog appends here land cleanly on main but won't be visible to those worktrees until they rebase."* Proceed with the write — reviewing on main is the canonical author. (Two reviewing sessions writing in parallel from different worktrees rely on the milestone-scoped append rule above, not on this check.)
320
+
315
321
  Read `.forge/refactor-backlog.yml`. Next ID = max + 1. Append:
316
322
 
317
323
  ```yaml
@@ -350,7 +356,7 @@ Idempotent: a backlog with no terminal items and all-canonical statuses is left
350
356
 
351
357
  ### Route
352
358
 
353
- **HEALTHY/WARNINGS (accepted):** set `current.status: complete` in `milestone-{id}.yml`, then regenerate `index.yml` via the `forge` **Rollup** (do not hand-edit index). *"Milestone [{name}] complete. {N} backlog items."* Beads: `bd complete`.
359
+ **HEALTHY/WARNINGS (accepted):** set `current.status: complete` in `milestone-{id}.yml` (only after the Human Verification Gate in Phase Handoff is satisfied), then regenerate `index.yml` via the `forge` **Rollup** (do not hand-edit index). *"Milestone [{name}] complete. {N} backlog items."* Beads: `bd complete`.
354
360
 
355
361
  **CRITICAL:** Don't complete. A) Fix->`planning` fix mode->re-verify->re-review. B) Accept risk->doc in report->complete.
356
362
 
@@ -400,8 +406,9 @@ If the milestone's phases produced `contract.md` files (planning Step 6.1 Tier 1
400
406
  ## Phase Handoff
401
407
 
402
408
  1. Confirm report + backlog
403
- 2. **Run promoted-milestone completion hook** (above) if `milestone.origin` set
404
- 3. **Run Contract Landing** (above) for any cross-layer phases — fold ratified contracts into their ADRs
405
- 4. Set `current.status: complete` and `current.completed_at: "<ISO 8601 timestamp>"` in `milestone-{id}.yml`, then regenerate `index.yml` via the `forge` **Rollup**
406
- 5. **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 .`).
407
- 6. *"Milestone [{name}] complete. Report: `.forge/audits/milestone-{id}-health-report.md`. {N} backlog items. `/forge` or backlog."*
409
+ 2. **Human Verification Gate (hard block)** — Read `current.human_verified` from `milestone-{id}.yml`. If **absent/null** → **do not complete the milestone.** Code-clean + review-clean is not enough to close (FORGE.md → Human Verification Gate). The milestone needs an explicit human sign-off, normally captured by `verifying`. Either route the user back to verify (then re-review) or capture the sign-off / recorded override here and write `current.human_verified` before proceeding. A recorded override (`method: override`) satisfies this check but **must** be surfaced in the health report. Never silently complete past an unset gate.
410
+ 3. **Run promoted-milestone completion hook** (above) if `milestone.origin` set
411
+ 4. **Run Contract Landing** (above) for any cross-layer phases fold ratified contracts into their ADRs
412
+ 5. Set `current.status: complete` and `current.completed_at: "<ISO 8601 timestamp>"` in `milestone-{id}.yml`, then regenerate `index.yml` via the `forge` **Rollup**
413
+ 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 .`).
414
+ 7. *"Milestone [{name}] complete. Report: `.forge/audits/milestone-{id}-health-report.md`. {N} backlog items. `/forge` or backlog."*
@@ -42,6 +42,7 @@ Read: .github/workflows/* → CI config (ci-check mode, analyst CI sub-check)
42
42
 
43
43
  1. Spawn tester agent via Agent tool, mode = `analyst`:
44
44
  - Pass: milestone id + name, scope paths, stack summary from project.yml
45
+ - **Pass `model` param** from `project.yml` model routing (`models.skills.testing` → `models.default` → parent). Display: *"Spawning tester (analyst) with model: {model} (from {source})"*
45
46
  - Agent scans test files, runs suite with traces/retries, greps anti-patterns
46
47
  2. Agent writes `.forge/testing/suite-health.md`:
47
48
  - **Coverage Gaps** — untested surfaces, missing layers
@@ -106,6 +107,7 @@ Refusal message wording is contract (NFR-009 requires story ID + exact missing f
106
107
  - If project has existing test runner, match it. Don't introduce a second.
107
108
  3. Spawn tester agent via Agent tool, mode = `author`:
108
109
  - Pass: milestone id + name, layer, runner, scope paths, stack summary
110
+ - **Pass `model` param** from `project.yml` model routing (`models.skills.testing` → `models.default` → parent). Display: *"Spawning tester (author) with model: {model} (from {source})"*
109
111
  - Agent scaffolds tests following baked flake-resistant rules (see tester.md)
110
112
  4. Agent commits atomically per executor conventions.
111
113
 
@@ -266,6 +266,47 @@ No-op guarantee: detection exits clean (no prompt) when the backlog is absent, u
266
266
 
267
267
  `upgrading` never edits the backlog directly — migration runs via `quick-tasking`.
268
268
 
269
+ ### Pre-0.28.0 fully-ignored `.claude/`
270
+
271
+ Run from project root:
272
+
273
+ ```bash
274
+ F=.gitignore
275
+ [ -f "$F" ] || exit 0
276
+ # bare ".claude/" or ".claude" rule (no negation lines un-ignoring product files)
277
+ if grep -qE '^[[:space:]]*\.claude/?[[:space:]]*$' "$F" \
278
+ && ! grep -qE '^![[:space:]]*\.claude/(skills|agents|hooks|settings\.json)' "$F"; then
279
+ echo "broad-ignore"
280
+ fi
281
+ ```
282
+
283
+ If the script prints `broad-ignore`, surface:
284
+
285
+ ```
286
+ .claude/ fully ignored — Forge worktrees + hooks silently break (Forge 0.28.0)
287
+ ─────────────────────────────────────────────────────────────────────────────
288
+ Your .gitignore ignores all of `.claude/` without a carve-out. That means:
289
+ • git worktrees (Forge or the app's) have no `.claude/skills/`, so `/forge`
290
+ is missing — typing `/forge` in a worktree prints "Unknown command".
291
+ • M10 worktrees come up with no hooks (forge-claim-check, branch-guard,
292
+ session-id) — coordination + ADR-008 protection are silently disabled.
293
+
294
+ These are PRODUCT files, not per-machine config. The fix is a narrow
295
+ carve-out: track skills/agents/hooks/settings.json; keep settings.local.json,
296
+ projects/, worktrees/ ignored.
297
+
298
+ A migration guide is available at: .forge/migrations/0.28.0-worktree-root.md
299
+ (canonical: https://github.com/Attuned-Media/forge/blob/main/docs/migrations/0.28.0-worktree-root.md)
300
+
301
+ Apply the carve-out now? (yes/no/show guide)
302
+ ```
303
+
304
+ - **yes** → invoke `quick-tasking` skill with the migration guide as the task (edit `.gitignore` in place, `git rm -r --cached .claude/` to clear the ignore-cache, stage `.gitignore` + the four product trees, commit `chore(forge): track .claude/ product files`). The skill MUST confirm with the user before staging anything outside the four product trees.
305
+ - **show guide** → read and display the file, then re-ask
306
+ - **no** → note in upgrade report. The framework keeps working in the main checkout but worktree-mode sessions (M10 / Claude Desktop folder-open) will be missing `/forge` and Forge hooks until migrated.
307
+
308
+ This is a Forge-product-tracking concern, not a state migration — `upgrading` never edits user `.gitignore` directly. Migration runs via `quick-tasking`.
309
+
269
310
  ### Future migrations
270
311
 
271
312
  Add new detection blocks here for each Forge version that changes file layout. Pattern:
@@ -236,6 +236,19 @@ Items that can't be verified automatically (visual appearance, real-time behavio
236
236
  3. [ ] External: Email sends correctly via SendGrid
237
237
  ```
238
238
 
239
+ ## Human Verification Gate (close precondition)
240
+
241
+ Code-level PASS is necessary but **not sufficient** to close a milestone. Before handing off, capture an explicit human sign-off — this is the hard gate that `reviewing`-complete and `orchestrating`-teardown both check (see FORGE.md → Human Verification Gate). It is a precondition, not a peer option: never present milestone close / teardown as an alternative to running this.
242
+
243
+ Run on a PASSED verdict (and after any **HUMAN VERIFICATION NEEDED** items are resolved):
244
+
245
+ 1. Surface what the human should confirm — the Observable Truths, any "Human Verification Items", and any milestone-specific manual check (device session, visual pass, real-device / e2e walk).
246
+ 2. Prompt explicitly: *"Code verification passed. Have you verified the work yourself (device session / visual / e2e walk)? This is required before the milestone can close. [verified | not yet | close without verifying]"*
247
+ 3. Record the response in `current.human_verified` in `.forge/state/milestone-{id}.yml`:
248
+ - **verified** → `{at: "<ISO 8601>", method: "<how they verified>", notes: "<optional>"}`
249
+ - **not yet** → leave `human_verified` unset. **Do NOT route to reviewing** — the milestone cannot close. Tell the human what's outstanding and stop. Re-run this gate after they've done it.
250
+ - **close without verifying** → recorded override: `{at: "<ISO 8601>", method: "override", override: true, reason: "<required free text>"}`. Note the override in the verification report. Satisfies the gate but is flagged downstream.
251
+
239
252
  ## Re-Verification Mode
240
253
 
241
254
  After gap closure:
@@ -289,9 +302,10 @@ Only suggest at 3+ occurrences. One-off issues are noise.
289
302
 
290
303
  After PASSED verdict:
291
304
 
292
- 1. **Persist** — Confirm verification report documented, desire-path files written to `.forge/state/desire-paths/`
293
- 2. **Update state** — Set `current.status` to `reviewing` in `.forge/state/milestone-{id}.yml`
294
- 3. **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 .`).
295
- 4. **Recommend clear:** *"State synced. `/clear` then `/forge` for reviewing."*
305
+ 1. **Human Verification Gate** — capture explicit sign-off and write `current.human_verified` (see "Human Verification Gate" above). 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.
306
+ 2. **Persist** — Confirm verification report documented, desire-path files written to `.forge/state/desire-paths/`
307
+ 3. **Update state** Set `current.status` to `reviewing` in `.forge/state/milestone-{id}.yml`
308
+ 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 .`).
309
+ 5. **Recommend clear:** *"State synced. `/clear` then `/forge` for reviewing."*
296
310
 
297
311
  If GAPS found, route back to planning in gap-closure mode. Context clear applies after re-verified PASSED verdict.
@@ -51,8 +51,8 @@ Auto-detects complexity. Override: "Use Quick/Standard/Full tier."
51
51
  | Architectural decisions | `architecting` | Full |
52
52
  | Break work into tasks with gates | `planning` | Standard, Full |
53
53
  | Build with deviation rules + atomic commits | `executing` | All |
54
- | Prove work delivers on goals (+ M9 e2e validation gate when `e2e:true` stories present) | `verifying` | Standard, Full |
55
- | Audit health + catalog refactoring (+ M9 e2e soft-cap, orphan-test, flake-rate audits) | `reviewing` | Standard, Full |
54
+ | Prove work delivers on goals (+ M9 e2e validation gate when `e2e:true` stories present; captures the Human Verification Gate sign-off) | `verifying` | Standard, Full |
55
+ | Audit health + catalog refactoring (+ M9 e2e soft-cap, orphan-test, flake-rate audits; hard-blocks completion on the Human Verification Gate) | `reviewing` | Standard, Full |
56
56
  | Small scoped fix | `quick-tasking` | Quick |
57
57
  | UI with design system | `designing` | When UI |
58
58
  | Security review | `securing` | When auth/data/API |
@@ -102,6 +102,8 @@ models:
102
102
  **Precedence:** `skills.X` → `default` → parent model.
103
103
  **All advisory** — forge displays expected model at each transition, suggests `/model` on mismatch. Review gate logs model used. Cannot auto-switch.
104
104
 
105
+ **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. Resolve using the same precedence chain (`models.skills.{skill}` → `models.default` → parent) and surface it: *"Spawning {role} with model: {model} (from {source})"*.
106
+
105
107
  ## Agents
106
108
 
107
109
  | Agent | Role | Tools | When |
@@ -145,7 +147,7 @@ State lives in `.forge/`:
145
147
  **Milestones** group phases into concurrent streams. Own state file — no conflicts across sessions. Can be deferred (frozen in place) or archive-deleted.
146
148
  `index.yml status` gates routing: `not_started | active | deferred | complete`.
147
149
  **Format**: YAML for machine state, Markdown for human content.
148
- **`current.status` is authoritative.** Complete only at `current.status == complete`. 100% tasks ≠ done — still needs verifying + reviewing.
150
+ **`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).
149
151
 
150
152
  ### State Commit Protocol
151
153
 
@@ -160,9 +162,38 @@ State-sync commits are separate from per-task code commits (which stay atomic du
160
162
 
161
163
  ### State Ownership (multi-worktree safety)
162
164
 
163
- - `state/milestone-{id}.yml` is the **single source of truth**; exactly one agent owns it at a time.
165
+ `.forge/` is **code-like**: it branches with the repo, syncs via git, atomic-commits with the work it describes. Worktrees get their own `.forge/` checkout by construction — that's not configuration, that's git. The framework's job is to specify how each *kind* of `.forge/` file behaves under that model.
166
+
167
+ **Every `.forge/` artifact falls into one of five sharing classes.** The class determines the write rule across worktrees:
168
+
169
+ | Class | Files | Write rule |
170
+ |---|---|---|
171
+ | **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. |
172
+ | **Derived** | `state/index.yml` | Never hand-edited. Regenerated by Rollup 1.0 from `milestone-*.yml`. Only the main/orchestrator session runs rollup. |
173
+ | **Append-only shared** | `releases.yml` (version reservations), `state/desire-paths/*` (one file per observation) | Many writers OK; structure prevents collision. `releases.yml` uses the Version Reservation Protocol (append + commit + push before depending on the slot); desire-paths use distinct filenames so two writers never touch the same file. |
174
+ | **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) | Two rules: **(a) Write only to YOUR milestone's block.** Different milestone blocks → git merges cleanly. **(b) Within a block, append-only.** Locked decisions get *struck through* (`~~old~~`) rather than rewritten in place; new sub-bullets appended below existing ones. Same-line concurrent edits become impossible by construction. |
175
+ | **Stable shared** | `project.yml`, `constitution.md`, `design-system.md`, `roadmap.yml`, `FORGE.md` | Rarely changes after init. Canonical on main. Worktrees lag and pull on rebase. No special protocol — git handles it because the conflict surface is tiny. |
176
+
177
+ **Cross-tree write awareness.** When a session edits a shared-mutable file's block whose milestone is **live in a different worktree** (e.g. main writes to `context.md`'s `## M9` block while m9 is running in a worktree), the edit lands cleanly on main but is **invisible to the worktree until it pulls/rebases**. This is identical to how `src/foo.ts` works under branches — there's nothing pathological about it; it's branch divergence. The framework surfaces the situation so the operator can decide:
178
+
179
+ - **`forge` Step 1** in main checks `git worktree list` and surfaces live Forge worktrees at boot — *"m9 is live in worktree X at e94b6bb. Edits to m9 state belong there; shared docs edited here need a sync."*
180
+ - **`discussing`** (before writing to `context.md`'s milestone block) and **`reviewing`** (before writing to `refactor-backlog.yml`) emit a one-line warning when the target block's milestone is live elsewhere. The warning informs, never blocks — main is often the canonical author of these files.
181
+ - **Worktree-side rebase check** (opt-in via `forge.worktree_rebase_check: true` in `project.yml`) — when `forge` boots inside a worktree, it diffs `.forge/` between the worktree's HEAD and `main`; if shared-mutable files have changed on main, it offers `git restore --source main -- <files>` or a rebase.
182
+
183
+ **Why not global `.forge/`?** A common instinct is "make `.forge/` shared so context propagates automatically." It breaks four load-bearing invariants:
184
+ 1. **Decisions are contracts** — they're versioned with the code they govern; severing them from branches breaks the contract.
185
+ 2. **State survives machine loss** — the multi-laptop pull-from-any-clone story depends on `.forge/` being in git.
186
+ 3. **History time-travel** — `git checkout` an old commit and you see the plan/decisions as they were; global state makes that impossible.
187
+ 4. **Git is the concurrency detector** — without it, two sessions writing the same field becomes silent last-write-wins.
188
+
189
+ Per-worktree `.forge/` with a sharing taxonomy is the model. See [ADR-011](../docs/decisions/ADR-011-shared-state-taxonomy.md) for the longer version.
190
+
191
+ ### State Ownership rules (quick reference)
192
+
193
+ - `state/milestone-{id}.yml` is the **single source of truth**; exactly one agent owns it at a time. **Other worktrees never write it** — promoted from advice to explicit rule, because catching yourself before the first wrong edit is the part the framework can help with.
164
194
  - Worktree / parallel agents write **only** their own milestone file and **append** desire-path files. They **never** write `index.yml`.
165
195
  - `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.
196
+ - For shared-mutable files: write only your milestone's block; within a block, append-only (strike through, don't rewrite).
166
197
  - Same-milestone parallel work is out of scope, guarded by the M10 claim layer.
167
198
 
168
199
  ### Version Reservation Protocol
@@ -211,6 +242,16 @@ verification:
211
242
  - Empty commands = no gate (opt-out)
212
243
  - `verification.e2e_soft_cap` (default 10) — advisory cap on `e2e:true` stories per milestone surfaced by the `reviewing` skill. Soft — never blocks.
213
244
 
245
+ ### Human Verification Gate (close precondition)
246
+
247
+ **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").
248
+
249
+ - **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.
250
+ - **Enforced (hard block)** at both close points — `reviewing` refuses to set `current.status: complete`, and `orchestrating` Step 5 refuses teardown, unless `current.human_verified` is present.
251
+ - **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.
252
+
253
+ 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.
254
+
214
255
  ## Beads Integration (Optional)
215
256
 
216
257
  With Beads installed: `bd prime` (session context), `bd ready` (unblocked tasks), `bd complete` (update deps), `bd compact` (summarize old). Without Beads, `.forge/state/` + Session Memory suffice.
@@ -0,0 +1,168 @@
1
+ # Migration Guide: Repo-scoped worktree root (Forge 0.28.0)
2
+
3
+ Applies to projects using M10 orchestration (experimental). Two related concerns ship under one version:
4
+
5
+ 1. **Worktree dir default changes** from a shared `../forge-worktrees/` to a per-repo `../<repo>-worktrees/` (see [ADR-010](../decisions/ADR-010-repo-scoped-worktree-root.md)). Live worktrees keep working; only new ones use the new default.
6
+ 2. **`.gitignore` carve-out for Forge product files** — repos that ignore `.claude/` wholesale silently break worktree mode (`/forge` is "Unknown command" inside any worktree). 0.28.0 ships detection + a guided fix.
7
+
8
+ If you don't use M10, the worktree-dir change has no effect. If your `.gitignore` is broadly ignoring `.claude/`, the second part still applies (the same gap also affects Claude Desktop's native worktree switcher).
9
+
10
+ ## Part 1 — Worktree dir default
11
+
12
+ ### What changed
13
+
14
+ Before:
15
+
16
+ ```bash
17
+ git worktree add ../forge-worktrees/${anchor} main # shared, cwd-resolved
18
+ ```
19
+
20
+ After:
21
+
22
+ ```bash
23
+ # orchestration.worktree_root from .forge/project.yml, default ../<repo>-worktrees
24
+ # Relative paths resolve against the REPO ROOT (not shell cwd). Absolute + leading ~ honored.
25
+ git worktree add ${wt_root}/${anchor} main
26
+ ```
27
+
28
+ The resolved absolute path is recorded in `lifecycle.worktree_path` and read verbatim by teardown + crash-recovery — so the config knob is forward-looking, not retroactive.
29
+
30
+ ### Detection
31
+
32
+ ```bash
33
+ # Are any of your active milestones pointing at the old shared dir?
34
+ grep -rE '^\s*worktree_path:.*forge-worktrees' .forge/state/ 2>/dev/null | head
35
+ ```
36
+
37
+ Matches = pre-0.28.0 worktrees still in flight. **Leave them alone** — they keep working off their recorded path. Only **new** worktrees pick up the new default.
38
+
39
+ ### Migration
40
+
41
+ **Do nothing.** This is a new-worktree-only change. Future orchestration runs in this repo will create their worktrees in `../<repo>-worktrees/<anchor>` instead of `../forge-worktrees/<anchor>`. Existing worktrees keep functioning; their `lifecycle.worktree_path` is honored.
42
+
43
+ ### Opting back into the old layout
44
+
45
+ If you want the old behavior (shared dir, e.g. because you've already got tooling pointed at `../forge-worktrees/`), set in `.forge/project.yml`:
46
+
47
+ ```yaml
48
+ orchestration:
49
+ worktree_root: "../forge-worktrees" # relative to repo root
50
+ ```
51
+
52
+ ### Other layout choices the new key enables
53
+
54
+ ```yaml
55
+ orchestration:
56
+ worktree_root: "~/.forge/worktrees/<this-repo>" # central home dir
57
+ # or
58
+ worktree_root: "../wt/<this-repo>" # shared dir, repo-namespaced subfolder
59
+ # or
60
+ worktree_root: "/abs/path/to/somewhere" # absolute
61
+ ```
62
+
63
+ **Do not** point this inside the working tree (e.g. `.worktrees/`) — file watchers, language-server indexers, test discovery, and bundlers commonly recurse into the second checkout regardless of `.gitignore`. ADR-010 has the longer analysis.
64
+
65
+ ### Relocating a live worktree (only if you really need to)
66
+
67
+ Use `git worktree move` — it updates the gitdir pointer. A bare `mv` leaves the pointer stale and the worktree unusable.
68
+
69
+ ```bash
70
+ # from the repo root
71
+ git worktree move ../forge-worktrees/m-9-abc12345 ../<repo>-worktrees/m-9-abc12345
72
+ # then update the recorded path in milestone state:
73
+ # .forge/state/milestone-9.yml → lifecycle.worktree_path
74
+ ```
75
+
76
+ Confirm the worktree opens cleanly (`cd <new-path> && git status`) before editing state. **Don't relocate a worktree that's an active orchestration run** without checking with the running session first — its claim state references that path.
77
+
78
+ ## Part 2 — `.gitignore` carve-out for Forge product files
79
+
80
+ ### Why this matters
81
+
82
+ Forge installs into `.claude/` (skills, agents, hooks, `settings.json`). Many Claude Code starters add `.claude/` to `.gitignore` wholesale. Consequences:
83
+
84
+ - **`/forge` is "Unknown command:" in any git worktree.** Worktrees only check out *tracked* files. With `.claude/` fully ignored, `.claude/skills/forge/` doesn't exist in worktrees, so the slash command isn't loaded. Same applies to Claude Desktop's native worktree switcher.
85
+ - **M10 worktrees come up with no hooks.** `forge-claim-check.sh`, `forge-branch-guard.sh`, `forge-session-id.sh` all live in `.claude/hooks/` — ignored = absent = silently disabled. ADR-007 / ADR-008 protection are off.
86
+
87
+ The fix: track product files, keep per-machine/scratch files ignored.
88
+
89
+ ### Detection
90
+
91
+ `upgrading` runs this automatically; you can run it by hand too:
92
+
93
+ ```bash
94
+ F=.gitignore
95
+ [ -f "$F" ] || exit 0
96
+ if grep -qE '^[[:space:]]*\.claude/?[[:space:]]*$' "$F" \
97
+ && ! grep -qE '^![[:space:]]*\.claude/(skills|agents|hooks|settings\.json)' "$F"; then
98
+ echo "broad-ignore — apply the carve-out below"
99
+ fi
100
+ ```
101
+
102
+ ### Migration
103
+
104
+ Replace any bare `.claude/` rule with this carve-out:
105
+
106
+ ```gitignore
107
+ # Claude Code — track Forge product files, ignore per-machine + scratch
108
+ .claude/*
109
+ .claude/.DS_Store
110
+ !.claude/settings.json
111
+ !.claude/skills/
112
+ !.claude/agents/
113
+ !.claude/hooks/
114
+ !.claude/commands/
115
+ .claude/settings.local.json
116
+ .claude/projects/
117
+ .claude/worktrees/
118
+ ```
119
+
120
+ Then refresh git's view of the directory:
121
+
122
+ ```bash
123
+ git rm -r --cached .claude/ 2>/dev/null # drop the wholesale-ignored cache
124
+ git status .claude/ # eyeball what's now untracked
125
+
126
+ # Stage only the four product trees — explicitly, never `git add .claude/`
127
+ git add .gitignore \
128
+ .claude/settings.json \
129
+ .claude/skills/ \
130
+ .claude/agents/ \
131
+ .claude/hooks/
132
+
133
+ # Sanity check — no per-machine files snuck in
134
+ git status # settings.local.json, projects/, worktrees/ should still be untracked-and-ignored
135
+ git diff --cached --stat | head
136
+
137
+ git commit -m "chore(forge): track .claude/ product files (skills, agents, hooks, settings)"
138
+ ```
139
+
140
+ After the commit lands on `main`, every **new** worktree (Forge's or the app's) inherits `/forge` and the hooks. Existing worktrees will see them on their next pull/rebase.
141
+
142
+ ### What stays ignored, and why
143
+
144
+ | Path | Why it stays ignored |
145
+ |---|---|
146
+ | `.claude/settings.local.json` | Per-machine permission allowlists. Build-specific. |
147
+ | `.claude/projects/` | Claude Code conversation transcripts — privacy + repo bloat. |
148
+ | `.claude/worktrees/` | App-managed scratch worktrees — per-machine ephemera. |
149
+ | `.claude/.DS_Store` | macOS metadata. |
150
+ | (project-local) `.mcp.json` if you already ignore it | Per-machine MCP wiring. |
151
+
152
+ ### One-shot vs guided
153
+
154
+ `upgrading` detects this pattern and offers a `quick-tasking` migration that applies the change above for you. Either path works — by-hand is fine if you want to eyeball each step. The migration is **never auto-applied**; `.gitignore` is user-owned.
155
+
156
+ ## Validation
157
+
158
+ After the worktree-root change ships and you've reopened a session:
159
+
160
+ - `cat .forge/project.yml` includes the `orchestration:` block (uncomment `worktree_root` only if you want a non-default value).
161
+ - Run an M10 milestone — confirm the new worktree appears at `../<your-repo>-worktrees/m-{id}-{session_id}/`.
162
+ - `cat .forge/state/milestone-{id}.yml | grep worktree_path` shows an **absolute** path, not the relative input.
163
+
164
+ After the gitignore migration:
165
+
166
+ - `git ls-files .claude/skills/forge/SKILL.md` returns the file.
167
+ - Open or create a worktree, `cd` into it, run `/forge` — the command exists.
168
+ - (If M10) Open a new orchestrating session in the worktree — `.session-id` is written, hooks fire.
@@ -0,0 +1,68 @@
1
+ # Migration Guide: Shared-state taxonomy (Forge 0.29.0)
2
+
3
+ Applies to any Forge project that uses git worktrees (M10 or hand-rolled). This is a **documentation + skill-behavior** change, not a state-file change — there is nothing in `.forge/` to migrate. Read this once, then the new skill behavior catches the situations.
4
+
5
+ ## What changed and why
6
+
7
+ Forge's `State Ownership (multi-worktree safety)` section in `FORGE.md` previously documented two file classes — `state/milestone-{id}.yml` (single-owner) and `state/index.yml` (derived) — and was silent on every other file in `.forge/`. In practice three more classes exist, and the riskiest one (shared mutable — `context.md`, `refactor-backlog.yml`) had no rule.
8
+
9
+ A worked example from canvaz on 2026-06-15: a main-session edit to `context.md`'s `## M9` block was invisible to the live m9 worktree, because the worktree was branched off main earlier and reads `.forge/` from its own checkout. The operator had to deduce the sync gap from first principles, and nearly committed `milestone-9.yml` edits from main on the first attempt before catching themselves.
10
+
11
+ 0.29.0 closes the gap with **taxonomy + visibility + structural rules**, not with a state migration. See [ADR-011](../decisions/ADR-011-shared-state-taxonomy.md) for the longer version (including why a "global `.forge/`" approach is wrong).
12
+
13
+ ## The 5-class taxonomy (read this once)
14
+
15
+ | Class | Examples | Write rule |
16
+ |---|---|---|
17
+ | **Single-owner mutable** | `state/milestone-{id}.yml`, `phases/milestone-{id}/`, `research/milestone-{id}.md` | One writer at a time; other worktrees **never** touch it. Explicit rule. |
18
+ | **Derived** | `state/index.yml` | Never hand-edited; regenerated by Rollup 1.0. Only main/orchestrator runs rollup. |
19
+ | **Append-only shared** | `releases.yml`, `state/desire-paths/*` | Many writers OK; structure prevents collision. Append + commit + push before depending on the slot. |
20
+ | **Shared mutable** | `context.md`, `refactor-backlog.yml`, `requirements/m{N}.yml` | (a) Write only to YOUR milestone's block. (b) Within a block, append-only — strike through (`~~old~~`) superseded decisions instead of rewriting in place. |
21
+ | **Stable shared** | `project.yml`, `constitution.md`, `design-system.md`, `roadmap.yml`, `FORGE.md` | Rarely changes after init; canonical on main; worktrees lag and pull on rebase. No protocol — git handles it. |
22
+
23
+ The full canonical version lives in `FORGE.md` → `State Ownership`.
24
+
25
+ ## What the framework now does automatically
26
+
27
+ After upgrading to 0.29.0:
28
+
29
+ - **`forge` Step 1 preflight (main checkout):** before Rollup, surfaces any live Forge worktrees and their owning milestones — *"m9 is live in worktree X at e94b6bb. Edits to its milestone state belong there; shared docs edited here need a sync."* No prescription, just visibility.
30
+ - **`discussing`** emits a one-line warning before writing to `context.md`'s milestone block if that milestone is live in another worktree.
31
+ - **`reviewing`** emits the same warning before appending to `refactor-backlog.yml` if any milestone is live in a worktree.
32
+ - **`discussing`** also documents the supersede protocol (strike-through old decisions, append new ones below) so concurrent edits across worktrees stay mergeable.
33
+
34
+ ## Optional: enable the worktree-side rebase check
35
+
36
+ Opt-in; default off. When enabled, `forge` boot **inside a worktree** diffs `.forge/` against `main` and offers to pull individual shared-mutable / stable-shared files when they've moved.
37
+
38
+ Add to `.forge/project.yml`:
39
+
40
+ ```yaml
41
+ forge:
42
+ worktree_rebase_check: true
43
+ ```
44
+
45
+ Trade-off: adds one git diff per `forge` boot inside worktrees. Worth it for projects that frequently update `context.md` / `constitution.md` on main while worktrees are mid-flight; overkill for projects where the main checkout is mostly read-only.
46
+
47
+ ## Existing decisions in context.md — do nothing
48
+
49
+ The taxonomy is descriptive: every existing decision in `context.md` already fits the new rules (it lives under a milestone heading, was append-only in practice). No rewrite needed. **New** writes follow the protocol (use strike-through to supersede).
50
+
51
+ If a past edit broke the rule (decision rewritten in place), don't go back and reconstruct strike-throughs — the history is in git. Just follow the rule going forward.
52
+
53
+ ## Detection (none needed)
54
+
55
+ There's nothing to detect on disk. The framework changes are in skills and `FORGE.md`. Upgrade and you have them.
56
+
57
+ ## Validation
58
+
59
+ After `npx forge-orkes upgrade`:
60
+
61
+ - `grep "5-class\|sharing classes" .forge/FORGE.md` returns hits in the `State Ownership` section.
62
+ - `grep "cross-tree liveness\|append-only" .claude/skills/discussing/SKILL.md` returns the Write Protocol additions.
63
+ - `grep "worktree-liveness\|worktree_rebase_check" .claude/skills/forge/SKILL.md` returns the Step 1 preflight.
64
+
65
+ Then, if you have a worktree active:
66
+
67
+ - `forge` in the main checkout surfaces the live worktree at the top of its boot output.
68
+ - Begin a discussion in main that touches a worktree-live milestone → `discussing` emits the cross-tree warning before its first write.
@@ -91,6 +91,16 @@ models:
91
91
  debugging: sonnet # Investigation needs solid reasoning
92
92
  discussing: sonnet # Conversation needs natural fluency
93
93
 
94
+ orchestration: # M10 multi-agent orchestration (experimental). Only consulted when M10 is installed.
95
+ # auto: true # false = never auto-route Standard/Full through orchestrating (install = consent; set false to opt out).
96
+ # worktree_root: "../<repo>-worktrees" # Where Forge worktrees live. Default: ../<repo-basename>-worktrees (per-repo sibling — see ADR-010).
97
+ # Relative paths resolve against the REPO ROOT (not shell cwd). Absolute paths and a leading ~ are honored.
98
+ # Editing only affects newly created worktrees; live worktrees keep their recorded lifecycle.worktree_path.
99
+ # Common overrides:
100
+ # ~/.forge/worktrees/<repo> — central home dir, keeps the parent dir of the repo clutter-free
101
+ # ../wt/<repo> — shared dir with per-repo subfolder
102
+ # Do NOT point this inside the repo working tree (file watchers + indexers will recurse into the worktree).
103
+
94
104
  risks: # What could go wrong?
95
105
  - risk: ""
96
106
  mitigation: ""
@@ -22,6 +22,21 @@ current:
22
22
  status: not_started # not_started | researching | discussing | planning | executing | verifying | reviewing | complete
23
23
  completed_at: null # ISO 8601 timestamp — set when status transitions to complete
24
24
  last_updated: null # ISO 8601 — set at each transition; rolled up into index.yml registry for resume ordering
25
+ human_verified: null # Human Verification Gate record — REQUIRED before status can become `complete`
26
+ # or an orchestration worktree can be torn down (FORGE.md → Human Verification Gate).
27
+ # Code-level PASS is necessary but never sufficient. Set by the `verifying` skill
28
+ # when a human signs off (device session, visual check, e2e walk — form is open).
29
+ # Shape when set:
30
+ # human_verified:
31
+ # at: "<ISO 8601>" # when the human confirmed
32
+ # method: "device-session" # free text: how they verified
33
+ # notes: "" # optional free text
34
+ # Recorded-override escape hatch (satisfies the gate, but flagged downstream):
35
+ # human_verified:
36
+ # at: "<ISO 8601>"
37
+ # method: "override"
38
+ # override: true
39
+ # reason: "headless milestone — no human-observable surface"
25
40
 
26
41
  # NO stored progress block. Progress percent is DERIVED on read, never stored.
27
42
  # The forge skill computes it from genuinely-maintained signals: