forge-orkes 0.14.0 → 0.17.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.14.0",
3
+ "version": "0.17.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"
@@ -51,6 +51,9 @@ requirements:
51
51
  Mark unknowns `[NEEDS CLARIFICATION]` — never guess.
52
52
 
53
53
  ### 5. Decompose Tasks
54
+
55
+ **Cross-layer first:** if this phase introduces/changes an interface one layer produces and another consumes (litmus: would an isolated agent have to guess the other's shape?), pin the delta in `contract.md` and either tag tasks `layer:` (Tier 1, one plan) or split into producer plan-NNa (pins contract) + consumer plan-NNb (`depends_on` the *frozen contract*, builds in parallel) -- Tier 2. See planning skill Step 6.1.
56
+
54
57
  ```xml
55
58
  <task type="auto|manual">
56
59
  <name>{Verb} {thing} {detail}</name>
@@ -109,3 +112,4 @@ must_haves:
109
112
  - **Horizontal slicing**: models->routes->UI (prefer vertical)
110
113
  - **Gold-plating**: Beyond requirements
111
114
  - **Guessing**: Fill unknowns instead of `[NEEDS CLARIFICATION]`
115
+ - **Guessing a cross-layer shape**: splitting layers without pinning `contract.md` first -- the consumer ends up guessing the producer's shape. Pin the contract, then split.
@@ -7,6 +7,17 @@ description: "Make architectural decisions: choose frameworks, design data model
7
7
 
8
8
  Make architectural decisions. Document rationale. Consider alternatives.
9
9
 
10
+ ## Vertical-Slice Bias
11
+
12
+ Architectural decisions should preserve the planning skill's slice-first decomposition. Favor designs that let the team ship thin end-to-end user journeys early:
13
+
14
+ - **Prefer feature-folder layouts** (`src/features/signup/{ui,api,data}.ts`) over strict layer-folder layouts (`src/models/`, `src/api/`, `src/components/`) when the project allows. Layer-folder layouts are not banned -- but they invite horizontal decomposition.
15
+ - **Avoid framework choices that force big-bang integration.** If picking framework A means UI cannot be wired until the whole data layer ships, that's a red flag -- document the trade-off in the ADR's Consequences section.
16
+ - **Contracts before completeness.** Define the minimal API contract a single slice needs. Resist designing the full API surface upfront -- successive slices extend it.
17
+ - **Data models grow per slice.** Start with the fields slice 1 needs. Add columns/entities as later slices require. Reject "design the whole schema first" unless a `slice_exception: data_migration` phase is planned.
18
+
19
+ When an architectural decision conflicts with vertical slicing (e.g., a framework that requires full backend before any UI is testable), surface the conflict explicitly in the ADR's **Trade-Offs** section.
20
+
10
21
  ## When to Use
11
22
 
12
23
  - Choosing a framework, library, or major dependency
@@ -7,6 +7,20 @@ description: "Systematic debugging when tests fail, features break, errors are c
7
7
 
8
8
  Every hypothesis tested, every dead end recorded.
9
9
 
10
+ ## Entry Path: Merge Conflict [Experimental — M10]
11
+
12
+ Invoked by `orchestrating` skill when `forge_queue_commit` returns `status: conflict`.
13
+
14
+ **Payload:** `{ conflicted_files: [...], base_sha, messages: [...], branch }`
15
+
16
+ **Workflow:**
17
+ 1. Inside agent's worktree: `git fetch origin main && git rebase ${base_sha}` (use payload ref).
18
+ 2. For each `conflicted_files[]` entry: inspect both sides via `git status` + `git diff`. Resolve per task context (or prompt user when intent unclear).
19
+ 3. After all resolved: `git add <files>` then `git rebase --continue`.
20
+ 4. Re-invoke `Skill(orchestrating)` with `action: retry-teardown` → orchestrating re-calls `forge_queue_commit`.
21
+
22
+ **Abort path:** if user aborts or resolution stalls, leave worktree in conflicted state and append `{ kind: "merge_conflict", branch, files }` to `lifecycle.blockers[]` in `.forge/state/milestone-{id}.yml`. Single-agent debugging entry paths below are unaffected.
23
+
10
24
  ## Scientific Method
11
25
 
12
26
  1. **Observe**: Exact behavior — error, repro steps, when it started
@@ -35,6 +35,16 @@ Execution-phase operational guidance below supplements the rules — it does not
35
35
  ### Scope Boundary
36
36
  Only fix issues DIRECTLY caused by the current task. Pre-existing warnings, tech debt, unrelated bugs → log to `.forge/deferred-issues.md`.
37
37
 
38
+ ### Slice Integrity (Execution-Side)
39
+
40
+ The planning skill enforces vertical slicing at plan-creation time. Executor responsibility: do not silently re-introduce horizontal decomposition.
41
+
42
+ - **Do not split a slice plan into "backend now, UI later"** under Rule 1/2/3. If the UI half of a slice is broken, fix it -- do not defer it. Deferring the UI breaks the slice's user-observable truth.
43
+ - **Do not collapse the slice into a stub** to pass verification. `key_links` must be real (component actually hits handler; handler actually persists). Stubbed links fail the verifying gate.
44
+ - If a slice genuinely cannot ship end-to-end (e.g., external API blocker), invoke **Rule 4 -- STOP, ask user.** Options: redefine the slice, declare a `slice_exception:` and continue, or defer the phase. Do not autonomously ship half a slice.
45
+
46
+ This rule does NOT override the 3-strike limit or scope boundary -- it sits alongside them.
47
+
38
48
  ## Native Task Tracking
39
49
 
40
50
  Use `TaskCreate`/`TaskUpdate`/`TaskList` for in-session visibility. `.forge/state/milestone-{id}.yml` remains the cross-session source of truth.
@@ -95,6 +105,30 @@ feat(auth-01): implement JWT-based login
95
105
  - Include integration test for login flow
96
106
  ```
97
107
 
108
+ ## Multi-Agent Claim Convention [Experimental — M10]
109
+
110
+ **Trigger:** active milestone state has `lifecycle.worktree_mode: active` (set by `orchestrating` skill). If absent or any other value → skip this section entirely; single-agent behavior unchanged.
111
+
112
+ **Pre-edit:** before the first `Edit` / `Write` / `MultiEdit` / `NotebookEdit` in a task, call MCP tool:
113
+
114
+ ```
115
+ forge_claim_files {
116
+ session_id: lifecycle.session_id,
117
+ files: [<absolute paths from task <files> manifest>],
118
+ ttl_seconds: 1800,
119
+ reason: "executing m{M}-{N} task <name>"
120
+ }
121
+ ```
122
+
123
+ **Branches:**
124
+ - **Full claim granted** → proceed with edits.
125
+ - **Partial rejection** → surface `rejected[].file`, `rejected[].owner_session`, `rejected[].expires_at` to user. Three options: **abort** task, **skip** rejected files (continue with granted subset), **wait** then retry after `expires_at`.
126
+ - **`DB_UNAVAILABLE`** → log warning, proceed. PreToolUse claim-check hook is defense-in-depth — coordination degrades to best-effort, isolation (worktree) still holds.
127
+
128
+ **End of task:** call `forge_release_claims { session_id, files: [...] }` after final commit. Plan-complete bulk release handled by `orchestrating` teardown.
129
+
130
+ See ADR-003 and `.claude/skills/orchestrating/SKILL.md`.
131
+
98
132
  ## Verification Gate
99
133
 
100
134
  After each task commit, run configured verification commands. Mechanical — not optional.
@@ -221,7 +255,21 @@ Log to `.forge/state/index.yml → desire_paths` (global, not per-milestone):
221
255
  - **User corrections**: Repeated correction matching a prior one → `user_correction`, increment count
222
256
  - **Agent struggles**: Multiple attempts or user guidance needed → `agent_struggle`
223
257
 
258
+ ## Cross-Layer Seam Check
259
+
260
+ **Trigger:** the phase was split by planning Step 6.1 into a **Tier-2** producer plan-NNa + consumer plan-NNb (both carry a `contract:` frontmatter path pointing at the same `contract.md`). Single-plan / Tier-1 (`layer:` tag, no split, contract honored inline) → skip; no seam check needed.
261
+
262
+ After **both** layer plans are committed, the executing flow owns one final **seam-check task** — there is no standing agent for this:
263
+
264
+ 1. **Read** the phase `contract.md` — `delta`, `producer_layer`, `consumer_layer`, `seam_check`, `status` (should already be `ratified` from the planning Tier-2 gate).
265
+ 2. **Merge** the layer branches/worktrees. If `lifecycle.worktree_mode: active`, the `orchestrating` teardown merges them; otherwise merge the sibling plan branches into the phase working tree.
266
+ 3. **Verify the seam** — run the assertion named in `contract.md` `seam_check` (a test, a type-check, or a structural grep) to prove the shape the producer emits matches what the consumer built against per `delta`.
267
+ 4. **Match** → commit the merge: `feat({phase}): seam check {integration_point}`. Contract stays `status: ratified`.
268
+ **Mismatch** → the consumer guessed wrong against a frozen contract → **Rule 1** fix on the consumer side. If the *contract itself* is wrong (producer can't emit the agreed shape) → **Rule 4** STOP, re-ratify with the user before proceeding.
269
+ 5. Leave the contract at `status: ratified` — the `reviewing` skill folds `delta` into the governing ADR (`status: absorbed`) at milestone landing. Do **not** absorb here.
270
+
224
271
  ## Phase Handoff
225
272
  1. Confirm persistence — summary documented, commits made, state updated, desire paths logged
226
- 2. Set `current.status` to `verifying`
227
- 3. Recommend: *"Tasks committed, state updated. `/clear` then `/forge` to continue with verifying."*
273
+ 2. **Run the Cross-Layer Seam Check** (above) if this phase was a Tier-2 contract split
274
+ 3. Set `current.status` to `verifying`
275
+ 4. Recommend: *"Tasks committed, state updated. `/clear` then `/forge` to continue with verifying."*
@@ -188,6 +188,8 @@ Tier + state → invoke via `Skill` tool. All phases use `Skill()`.
188
188
 
189
189
  **CRITICAL: NEVER `EnterPlanMode`.** "Planning" = `Skill(planning)`. Native plan mode writes wrong format, bypasses gates + state.
190
190
 
191
+ **Experimental:** if user invokes `orchestrating` skill (M10) and repo has it installed (`.claude/skills/orchestrating/` present + MCP server + claim-check hook), route through it **before** `executing` to bootstrap multi-agent worktree. Skill is opt-in per ADR-001; absent install → fall through to standard routing.
192
+
191
193
  ### Auto-Routing (Always Deterministic)
192
194
 
193
195
  **No menus.** Applies on first run and resume. Deterministic. Brief → route. Choices only at `complete` or corrupted.
@@ -225,6 +225,19 @@ Glob: src/**/index.{ts,tsx,js} # barrel exports
225
225
  Grep: src/ for "import.*from.*@/" # path aliases
226
226
  ```
227
227
 
228
+ ### Step 3.5: Architectural Layers
229
+
230
+ Detect distinct layers that hand a typed interface across a boundary — feeds the cross-layer contract detection in planning Step 6.1. A layer = a directory whose code is *produced for* or *consumed by* another (engine↔ui, core↔plugins, api↔web, native↔bindings).
231
+
232
+ ```bash
233
+ Bash: ls -d */ src/*/ 2>/dev/null # top-level + src subdirs as layer candidates
234
+ Grep: cross-boundary imports (e.g. ui importing engine types, generated bindings, ABI/descriptor/schema files)
235
+ ```
236
+
237
+ A single cohesive codebase with no internal producer→consumer boundary → **not** layered; leave `layers: []` (Step 6.1 no-ops). Only flag layers when one directory's output is another's typed input.
238
+
239
+ Present detected layers for confirmation: *"Detected layers: [{name → path}]. These hand interfaces across a boundary — confirm or correct."* Confirmed 2+ → written to `project.yml` `layers:` and seeded into `.forge/contracts/index.yml` at Finalize.
240
+
228
241
  ### Step 4: Present
229
242
 
230
243
  *"Project: {name} — {description}
@@ -269,6 +282,12 @@ User describes project → `.forge/project.yml`: name, goal, stack, constraints,
269
282
 
270
283
  Validate each term against `.forge/templates/interface-detection.md` type vocabulary. On unrecognized term, prompt: *"Did you mean [closest match]? Valid: browser | cli | api | desktop | native-apple | none."* Write validated answer as `interface: [...]` in project.yml.
271
284
 
285
+ ### Step 1.6: Architectural Layers
286
+
287
+ *"Will this project have distinct layers that hand a typed interface across a boundary (e.g. engine ↔ ui, core ↔ plugins, api ↔ web)? List them as name → path, or 'no' for a single-layer project."*
288
+
289
+ 2+ layers → write `layers:` to project.yml + seed `.forge/contracts/index.yml` at Finalize. Otherwise `layers: []` (planning Step 6.1 no-ops).
290
+
272
291
  ### Step 2: Design System
273
292
 
274
293
  *"UI library?"*
@@ -314,10 +333,11 @@ User selects per stack.
314
333
 
315
334
  ## Finalize
316
335
 
317
- 1. Write `.forge/project.yml` (all info + `verification`)
336
+ 1. Write `.forge/project.yml` (all info + `verification` + `layers`)
318
337
  2. Write `.forge/constitution.md`
319
338
  3. Write `.forge/design-system.md` (if configured)
320
- 4. Init state:
339
+ 4. Write `.forge/contracts/index.yml` (only if `layers:` has 2+ entries) — copy `.forge/templates/contracts-index.yml`, fill `layers:` from the confirmed list, leave `integration_points:` empty (first cross-layer phase populates them via planning Step 6.1)
340
+ 5. Init state:
321
341
  - `.forge/state/index.yml`:
322
342
  ```yaml
323
343
  milestones:
@@ -339,7 +359,7 @@ User selects per stack.
339
359
  task: null
340
360
  status: not_started
341
361
  ```
342
- 5. Templates as needed
362
+ 6. Templates as needed
343
363
 
344
364
  *"Initialized. Ready?"*
345
365
 
@@ -7,6 +7,26 @@ description: "Break work into executable tasks with verification gates. Enforces
7
7
 
8
8
  > **Do NOT use `EnterPlanMode`.** Output -> `.forge/phases/`.
9
9
 
10
+ ## Core Principle: Vertical Slicing
11
+
12
+ **Every phase and every plan MUST deliver a thin vertical slice -- a user-observable behavior reachable end-to-end (UI -> API -> data, or CLI -> core -> output).** Never decompose by horizontal layer (all models, then all APIs, then all UI). Horizontal slicing defers user-testable behavior until the last phase and amplifies integration risk.
13
+
14
+ Why:
15
+ - Each slice is testable, demoable, shippable on its own
16
+ - Bugs surface at the seam (where layers meet) on day one, not week three
17
+ - User can redirect direction after slice 1 instead of after the whole stack lands
18
+
19
+ Apply at three levels:
20
+ - **Roadmap (Step 5)**: phases are slices, not layers
21
+ - **Decompose (Step 6)**: plans are slices, not layers
22
+ - **Verify (Step 8)**: Slice Integrity gate -- hard fail on layer-only plans
23
+
24
+ Exceptions (must be explicitly justified in plan frontmatter `slice_exception:`):
25
+ - Foundational infra phase that no slice can reach yet (build setup, framework bootstrap)
26
+ - Shared library / cross-cutting refactor with no user-facing surface
27
+
28
+ If you find yourself writing a plan that only touches `src/models/`, `src/db/`, `src/schemas/`, `src/api/` (without UI/CLI counterpart), or `src/components/` (without data path) -- STOP. Merge with the slice that reaches the user, or claim an exception.
29
+
10
30
  ## Step 1: Resolution Gate
11
31
 
12
32
  Read `.forge/context.md` **Needs Resolution**. If unchecked `- [ ]` items:
@@ -77,11 +97,14 @@ Never write to top-level `.forge/requirements.yml` -- that path is deprecated.
77
97
  ### Case A: `roadmap.yml` missing (Full only)
78
98
 
79
99
  Create from `.forge/templates/roadmap.yml`:
80
- 1. Group by delivery boundaries
81
- 2. Inter-group dependencies
82
- 3. Phases (coherent, verifiable)
100
+ 1. **Group by vertical slice, NOT by layer.** Each phase = a thin end-to-end user journey. Wrong: `m1-models`, `m2-apis`, `m3-ui`. Right: `m1-user-can-sign-up`, `m2-user-can-post`, `m3-user-can-comment`.
101
+ 2. Inter-slice dependencies (slice B builds on artifact from slice A)
102
+ 3. Each phase has a one-sentence `goal:` written as user-observable outcome ("User can X"), never as "Build Y"
83
103
  4. Every FR -> one phase, no orphans
84
- 5. Waves: independent=1, dependent=2+
104
+ 5. Waves: independent slices=1, dependent slices=2+
105
+ 6. **Phase 1 must be demoable.** If phase 1 has no user-observable output, the roadmap is layered -- redesign.
106
+
107
+ Anti-pattern detection: scan proposed phase names. Reject if any phase name contains layer-only terms without a user verb: `models`, `schema`, `database`, `api-only`, `backend-only`, `ui-only`, `frontend-only`, `infrastructure` (unless tagged as exception phase).
85
108
 
86
109
  ### Case B: `roadmap.yml` exists, current milestone already in it
87
110
 
@@ -105,16 +128,62 @@ If a sibling milestone (e.g. m50) has state + requirements but is missing from `
105
128
 
106
129
  ## Step 6: Decompose Tasks
107
130
 
108
- Per phase (or feature, Standard tier):
131
+ ### Step 6.1: Cross-Layer Contract Detection
132
+
133
+ Before decomposing, classify whether this phase crosses a layer boundary with a *new or changing* contract. This decides single-plan vs a contract-pinned layer split. Read the project's layers from `project.yml` `layers:` (or `.forge/contracts/index.yml`; fallback: top-level source dirs).
134
+
135
+ **Trigger -- all three hold:**
136
+ 1. Work touches >= 2 declared layers (e.g. engine / blocks / ui).
137
+ 2. A struct / signature / ABI field / descriptor is *produced* by one layer and *consumed* by another.
138
+ 3. That interface is *new or changing* in this phase.
139
+
140
+ **Litmus (decisive):** "Would an agent building one layer in isolation have to GUESS the shape the other layer owns?" No (already specified in a durable contract) -> not cross-layer here.
141
+
142
+ **Two contract tiers:**
143
+ - **Durable** = the standing layer API. Lives in ADRs (`.forge/decisions/`) + constitution, indexed in `.forge/contracts/index.yml` (integration-point -> governing ADR). Stable + unchanged -> agents read the ADR; no per-phase artifact.
144
+ - **Per-phase delta** = the specific new/changed shape THIS phase introduces. Pinned in `.forge/phases/m{M}-{N}-{name}/contract.md` (from `.forge/templates/contract.md`); references its governing ADR; folded back into that ADR on landing.
145
+
146
+ **Classify:**
147
+
148
+ | Tier | Condition | Response |
149
+ |------|-----------|----------|
150
+ | 0 | Trigger fails | Normal decomposition (6.2). Nothing added. |
151
+ | 1 | Cross-layer delta, small / tightly sequential | Write `contract.md`; tag tasks `layer:`; ONE plan. No interruption. |
152
+ | 2 | Cross-layer delta, cleanly separable, worth parallel sessions | Pin `contract.md`; split into plan-NNa (producer layer, pins contract) + plan-NNb (consumer layer, `depends_on` the contract). Ratify gate. |
153
+
154
+ **Liberal detect, conservative interrupt:** classify every phase. Unsure between 1 and 2 -> default **Tier 1** (write the doc, no interruption). Escalate to 2 only when confident the parallel split pays off.
155
+
156
+ **Tier-2 ratify gate** (the ONLY interruption; frame as contract-correctness, not "parallelize y/n"):
157
+ > *"This phase changes the {integration point} contract ({governing ADR}). Delta: [summary]. plan-NNa ({producer}) pins it; plan-NNb ({consumer}) builds against it in parallel. Is this contract shape correct?"*
158
+
159
+ Block the split until confirmed. Override ("keep it one plan") -> log to `state/index.yml` `desire_paths` (recurring overrides tune the threshold), fall back to Tier 1.
160
+
161
+ **Integration (Tier 2):** layer plans build isolated (per-layer worktrees). The phase's final task is a **seam check** owned by the executing flow (NOT a standing agent): merge the layer branches, verify the shape the producer emits matches what the consumer built against, per `contract.md`.
162
+
163
+ ### Step 6.2: Task Decomposition
164
+
165
+ Per phase (or feature, Standard tier). **Each plan = one vertical slice** -- except a sanctioned Tier-2 contract split (6.1), which divides one slice across producer/consumer layer plans reconciled at the seam check.
166
+
167
+ #### Slice-First Decomposition
168
+
169
+ Before writing any plan:
170
+ 1. List the user-observable behaviors this phase must deliver (from `requirements/m{N}.yml`)
171
+ 2. For each behavior, identify the full path: UI/CLI surface -> handler -> business logic -> persistence (only the parts that behavior needs)
172
+ 3. **One plan = one behavior end-to-end.** Plan touches every layer that behavior needs, not all of one layer.
173
+ 4. If a plan can only ship part of the path (e.g., UI without backend wired), it is NOT a slice -- restructure.
174
+
175
+ Plan naming reflects the slice: `plan-01-user-signs-up.md`, not `plan-01-models.md`.
176
+
177
+ #### File Layout
109
178
 
110
179
  1. `.forge/templates/plan.md` -> `.forge/phases/m{M}-{N}-{name}/plan-{NN}.md`
111
180
  - `{M}`=milestone, `{N}`=phase#, `{name}`=kebab, `{NN}`=seq
112
181
  - Ex: `.forge/phases/m3-2-providers/plan-01.md`
113
- 2. Frontmatter: phase, plan#, wave, deps
182
+ 2. Frontmatter: phase, plan#, wave, deps, `slice_exception:` (optional, see Core Principle)
114
183
  3. must_haves:
115
- - **Truths:** User-observable outcomes (3-7)
116
- - **Artifacts:** Must exist, substantive not stubs
117
- - **Key Links:** Connections between artifacts
184
+ - **Truths:** User-observable outcomes (3-7). MUST be phrased as something the user can see, click, or receive -- not "model X exists" or "table Y created".
185
+ - **Artifacts:** Must exist, substantive not stubs. Slice plans typically span 2-4 layers (e.g., component + handler + repo).
186
+ - **Key Links:** Connections between artifacts -- these prove the slice is wired, not stubbed.
118
187
  4. XML tasks (2-3/plan, 15-60 min):
119
188
 
120
189
  ```xml
@@ -144,20 +213,45 @@ Per phase (or feature, Standard tier):
144
213
  | `checkpoint:decision` | Pause for user choice between options |
145
214
  | `checkpoint:human-action` | Pause for manual action (email verification, 2FA) |
146
215
 
147
- ### Vertical Slices (Preferred)
216
+ ### Vertical Slices (Required)
217
+
148
218
  ```
149
- Plan 01: User feature (model + API + UI) → Wave 1
150
- Plan 02: Product feature (model + API + UI) → Wave 1
219
+ Plan 01: User can sign up (UI form + /api/signup + users table write) → Wave 1
220
+ Plan 02: User can log in (UI form + /api/login + session issue) → Wave 2 (uses table from 01)
221
+ Plan 03: User can post note (UI editor + /api/notes + notes table) → Wave 2 (uses auth from 02)
151
222
  ```
152
- Independent plans run parallel.
153
223
 
154
- ### Avoid Horizontal Layers
224
+ Each plan is independently demoable. Bugs at layer seams surface in plan 01.
225
+
226
+ ### Horizontal Layers (Anti-Pattern -- BLOCKED)
227
+
155
228
  ```
156
- Plan 01: All models → Wave 1
157
- Plan 02: All APIs → Wave 2 (depends on 01)
158
- Plan 03: All UI → Wave 3 (depends on 02)
229
+ Plan 01: All models → Wave 1
230
+ Plan 02: All APIs → Wave 2 (depends on 01)
231
+ Plan 03: All UI → Wave 3 (depends on 02)
159
232
  ```
160
- Sequential. Only when architecturally required.
233
+
234
+ This decomposition is **rejected by default** at Step 8 (Slice Integrity gate). To proceed, declare `slice_exception:` in plan frontmatter with one of:
235
+ - `infra_bootstrap` -- foundational setup with no user-reachable surface
236
+ - `shared_library` -- cross-cutting utility used by future slices
237
+ - `data_migration` -- one-shot schema/data change with no behavior added
238
+
239
+ Anything else: restructure into slices.
240
+
241
+ ### Anti-Pattern Auto-Detector
242
+
243
+ A plan fails Slice Integrity if ALL of:
244
+ - `must_haves.truths` contain only artifacts/internals (e.g., "Schema migration applied", "Model class created") with no user-visible verb (see, click, receive, fail-with-error)
245
+ - `must_haves.artifacts` paths all live under a single layer prefix (only `src/models/`, only `src/api/`, only `src/components/`)
246
+ - No `slice_exception:` declared
247
+
248
+ Detection runs in Step 8.
249
+
250
+ ### Contract-Driven Layer Split (Tier 2 exception)
251
+
252
+ When Step 6.1 flags a **Tier-2** cross-layer contract, splitting by layer is correct -- NOT the horizontal anti-pattern above. The difference:
253
+ - *Horizontal anti-pattern:* split by layer with no contract; each layer waits on the previous. Serializes.
254
+ - *Contract-driven split:* plan-NNb `depends_on` the **frozen contract** (pinned by NNa up front), not NNa's implementation -> both layers build in parallel (separate sessions/worktrees), reconciled at the seam check.
161
255
 
162
256
  ## Step 7: Test Specs (Optional)
163
257
 
@@ -249,7 +343,7 @@ Decision captured once, pre-code. Does not block planning.
249
343
 
250
344
  ## Step 8: Verify Plans
251
345
 
252
- 8 dimensions:
346
+ 9 dimensions:
253
347
  1. **Requirement Coverage** -- every req has task(s)
254
348
  2. **Task Completeness** -- files + action + verify + done
255
349
  3. **Deps** -- valid DAG, no cycles
@@ -258,6 +352,20 @@ Decision captured once, pre-code. Does not block planning.
258
352
  6. **Verification** -- must_haves trace to goal
259
353
  7. **Context** -- honors locked, excludes deferred
260
354
  8. **Spec Validity** -- valid syntax, correct paths
355
+ 9. **Slice Integrity (HARD GATE)** -- every plan delivers a vertical slice OR declares `slice_exception:`
356
+
357
+ ### Slice Integrity Check
358
+
359
+ For each plan, FAIL if all of these hold and no `slice_exception:` is declared:
360
+ - `must_haves.truths` lack a user-observable verb (`see`, `click`, `submit`, `receive`, `view`, `download`, `error`, `redirected`, `login`, `signup`, etc.) -- internal-only truths like "Schema applied", "Model registered", "Index built" do not satisfy
361
+ - `must_haves.artifacts` paths cluster in a single layer (all under `models/`, all under `api/`, all under `components/`, etc.)
362
+ - No file path crosses a layer boundary (e.g., a component plus its handler, a CLI plus its core)
363
+
364
+ **Exempt:** a Tier-2 contract-split plan (Step 6.1) carries both `layer:` and `contract:` frontmatter. It is a sanctioned single-layer plan reconciled at the seam check -- treat as auto-`slice_exception` (the *phase*, not the plan, owns the vertical slice). It passes without declaring `slice_exception:`.
365
+
366
+ Roadmap-level check: FAIL if phase 1 has no user-observable goal.
367
+
368
+ On fail: restructure plan(s) into slices, or declare `slice_exception:` with one of `infra_bootstrap | shared_library | data_migration` and a one-line rationale. Re-verify.
261
369
 
262
370
  Issues -> fix, re-verify. Max 3 cycles.
263
371
 
@@ -371,9 +371,21 @@ If the milestone being completed has `milestone.origin: {R-id}` set (promoted fr
371
371
  3. Update item: `status: resolved`, set `completed: "<ISO 8601 date>"`. Keep `promoted_to: {milestone-id}` intact for audit trail.
372
372
  4. Log in summary: *"Backlog item {R-id} → resolved (promoted milestone {id} complete)."*
373
373
 
374
+ ## Contract Landing (cross-layer phases)
375
+
376
+ If the milestone's phases produced `contract.md` files (planning Step 6.1 Tier 1/2), close their lifecycle before completing the milestone. The durable contract is the ADR; the per-phase `contract.md` is a working delta that must be folded back in.
377
+
378
+ 1. Glob `.forge/phases/m{id}-*/contract.md`.
379
+ 2. For each contract not yet `absorbed` (Tier-2 lands at `ratified` after the executing seam check; Tier-1 lands at `proposed` — both fold the same way now that the phase is verified):
380
+ - Fold `delta` into its `governing_adr` — amend the ADR in `.forge/decisions/`, or supersede it (`Status: Superseded by ADR-{NNN}`) if the shape changed materially.
381
+ - If a **new** integration point was introduced, add it to `.forge/contracts/index.yml` `integration_points:` (id, produces, consumes, governing_adr, summary).
382
+ - Set the contract's `status: absorbed`. The ADR is now authoritative; `contract.md` becomes history.
383
+ 3. Any contract with no `governing_adr` set (nothing to fold into) → warn: *"Contract {integration_point} has no governing ADR — file one in `.forge/decisions/` before close, or the durable contract drifts from code."* Advisory — does not block completion.
384
+
374
385
  ## Phase Handoff
375
386
 
376
387
  1. Confirm report + backlog
377
388
  2. **Run promoted-milestone completion hook** (above) if `milestone.origin` set
378
- 3. Set `current.status: complete` and `current.completed_at: "<ISO 8601 timestamp>"`
379
- 4. *"Milestone [{name}] complete. Report: `.forge/audits/milestone-{id}-health-report.md`. {N} backlog items. `/forge` or backlog."*
389
+ 3. **Run Contract Landing** (above) for any cross-layer phases — fold ratified contracts into their ADRs
390
+ 4. Set `current.status: complete` and `current.completed_at: "<ISO 8601 timestamp>"`
391
+ 5. *"Milestone [{name}] complete. Report: `.forge/audits/milestone-{id}-health-report.md`. {N} backlog items. `/forge` or backlog."*
@@ -0,0 +1,27 @@
1
+ # Phase Contract: {integration point}
2
+
3
+ Copy to `.forge/phases/m{M}-{N}-{name}/contract.md` when planning **Step 6.1** detects a cross-layer delta (Tier 1 or 2). Pins the NEW or CHANGED interface shape the producing and consuming layers must agree on for this phase, so an agent building one layer in isolation does not have to guess the other's shape.
4
+
5
+ Lifecycle: pinned by the producer plan (NNa) BEFORE the consumer plan (NNb) builds against it -> ratified at the Tier-2 gate -> folded into the governing ADR when the phase lands (`status: absorbed`). The durable contract is the ADR; this file is the working delta on top of it.
6
+
7
+ ---
8
+
9
+ ```yaml
10
+ contract:
11
+ integration_point: "" # e.g. "engine -> ui (block descriptor)"
12
+ governing_adr: "" # durable contract this delta extends, e.g. "ADR-026" (see .forge/contracts/index.yml)
13
+ producer_layer: "" # owns + pins the shape (plan-NNa), e.g. "engine"
14
+ consumer_layer: "" # builds against it (plan-NNb), e.g. "ui"
15
+ status: proposed # proposed | ratified | absorbed
16
+ delta: |
17
+ # The exact new/changed shape the consumer must NOT have to guess:
18
+ # struct fields, ABI fields + sizeof/layout, function signatures,
19
+ # enum values, units, ownership. ASCII only.
20
+ seam_check: "" # how integration verifies producer output == consumer expectation,
21
+ # e.g. "ui block_shape_projection_test asserts port count from descriptor"
22
+ ```
23
+
24
+ ## Notes
25
+ - One contract block per cross-layer delta. A phase changing two integration points pins two blocks (or two files).
26
+ - The consumer plan's `depends_on` points at THIS contract reaching `status: ratified`, NOT the producer plan's completion -- that is what lets the layers build in parallel.
27
+ - On landing: amend or supersede `governing_adr` to absorb `delta`, set `status: absorbed`. The ADR is then authoritative; this file becomes history.
@@ -0,0 +1,35 @@
1
+ # Durable cross-layer contract index
2
+ #
3
+ # Maps each standing integration point between layers to the ADR(s) that
4
+ # govern it. Planning Step 6.1 reads this to (a) know an integration point
5
+ # already has a durable contract, (b) decide whether the current phase
6
+ # CHANGES it, (c) point producer/consumer agents at the authoritative shape.
7
+ #
8
+ # Durable contracts live in the ADRs themselves (.forge/decisions/). This
9
+ # index is only the lookup. A phase that changes a contract pins a per-phase
10
+ # delta in its contract.md, then folds it back into the governing ADR.
11
+ #
12
+ # Copy to .forge/contracts/index.yml and fill in for your project.
13
+
14
+ # The project's architectural layers -- used by the cross-layer trigger
15
+ # (Step 6.1 condition 1: "work touches >= 2 declared layers").
16
+ layers:
17
+ - name: "" # e.g. "engine"
18
+ path: "" # e.g. "engine/"
19
+ # - name: "blocks"
20
+ # path: "blocks/"
21
+ # - name: "ui"
22
+ # path: "ui/"
23
+
24
+ # Standing integration points and their governing ADR(s).
25
+ integration_points:
26
+ - id: "" # e.g. "engine<->block"
27
+ produces: "" # producer layer, e.g. "engine"
28
+ consumes: "" # consumer layer, e.g. "blocks"
29
+ governing_adr: [] # e.g. ["ADR-001"]
30
+ summary: "" # one line: what the contract covers
31
+ # - id: "engine->ui"
32
+ # produces: "engine"
33
+ # consumes: "ui"
34
+ # governing_adr: ["ADR-026"]
35
+ # summary: "Block descriptor: UI projects ports/params/layout from the engine descriptor"
@@ -16,20 +16,32 @@ type: execute # execute | tdd
16
16
  wave: 1 # Execution wave (1 = no dependencies)
17
17
  depends_on: [] # Plan IDs that must complete first
18
18
  autonomous: true # false if contains checkpoints
19
+ layer: "" # name from project.yml layers[], or "" -- set by planning Step 6.1 cross-layer split (Tier 1/2)
20
+ contract: "" # path to this phase's contract.md (cross-layer delta this plan pins/consumes), if any
21
+
22
+ # Vertical slice declaration. A plan delivers a thin end-to-end user behavior
23
+ # (UI -> API -> data, or CLI -> core -> output). Plans that touch only ONE layer
24
+ # are rejected by the planning Slice Integrity gate unless slice_exception is set.
25
+ slice_exception: null # null | infra_bootstrap | shared_library | data_migration
26
+ slice_exception_rationale: "" # Required if slice_exception != null. One line.
19
27
 
20
28
  must_haves:
21
- truths: # Observable from user perspective when plan is done
22
- - "" # e.g., "User can see their profile page"
23
- - "" # e.g., "API returns user data as JSON"
24
- artifacts: # Files that must exist and be substantive (not stubs)
25
- - path: "" # e.g., "src/components/Profile.tsx"
26
- provides: "" # e.g., "User profile display with avatar and bio"
29
+ truths: # USER-observable when plan is done. Must contain a user verb
30
+ # (see, click, submit, receive, view, download, error, redirect).
31
+ # Internal-only truths like "Schema applied" do NOT satisfy.
32
+ - "" # e.g., "User submits signup form and lands on dashboard"
33
+ - "" # e.g., "Invalid email shows inline 'must be a valid email' error"
34
+ artifacts: # Files that must exist and be substantive (not stubs).
35
+ # Slice plans typically span 2-4 layers — paths should cross
36
+ # boundaries (component + handler + repo), not cluster in one dir.
37
+ - path: "" # e.g., "src/components/SignupForm.tsx"
38
+ provides: "" # e.g., "Signup form with email + password fields"
27
39
  min_lines: 30 # Stub detection threshold
28
- key_links: # Critical connections between artifacts
29
- - from: "" # e.g., "src/components/Profile.tsx"
30
- to: "" # e.g., "/api/users/[id]"
31
- via: "" # e.g., "fetch in useEffect"
32
- pattern: "" # e.g., "fetch.*api/users"
40
+ key_links: # Connections between layers — these prove the slice is wired
41
+ - from: "" # e.g., "src/components/SignupForm.tsx"
42
+ to: "" # e.g., "/api/signup"
43
+ via: "" # e.g., "fetch on submit"
44
+ pattern: "" # e.g., "fetch.*api/signup"
33
45
  ```
34
46
 
35
47
  ## Tasks
@@ -14,6 +14,12 @@ tech_stack:
14
14
  testing: "" # e.g., Vitest, Jest, Pytest
15
15
  other: [] # Additional key dependencies
16
16
 
17
+ layers: [] # Architectural layers — enables cross-layer contract detection (planning Step 6.1).
18
+ # Populated during init (brownfield: producer/consumer source dirs; greenfield: asked).
19
+ # Each entry: {name, path}. Empty/absent = single-layer project, detection no-ops.
20
+ # e.g. [{name: engine, path: src/engine/}, {name: ui, path: src/ui/}]
21
+ # Durable integration points + governing ADRs live in .forge/contracts/index.yml.
22
+
17
23
  interface: [none] # Surfaces this project exposes: browser | cli | api | desktop | native-apple | none
18
24
  # Array — e.g. [browser, api] for full-stack projects
19
25
 
@@ -1,6 +1,16 @@
1
1
  # Forge Roadmap Template
2
2
  # Copy to .forge/roadmap.yml and customize.
3
- # Phases are delivery boundaries — coherent, verifiable capabilities.
3
+ #
4
+ # Phases are VERTICAL SLICES — thin end-to-end user journeys shippable on their own.
5
+ # NOT horizontal layers (do NOT carve into "all models" / "all APIs" / "all UI" phases).
6
+ #
7
+ # Right: m1-user-can-sign-up, m2-user-can-post, m3-user-can-comment
8
+ # Wrong: m1-models, m2-apis, m3-ui
9
+ #
10
+ # Each phase's `goal:` must read as a user-observable outcome ("User can X"),
11
+ # never as "Build Y". Phase 1 must be demoable. The planning skill's
12
+ # Slice Integrity gate will reject layered roadmaps unless a `slice_exception:`
13
+ # is declared on the plan (infra_bootstrap | shared_library | data_migration).
4
14
 
5
15
  roadmap:
6
16
  # Milestones group phases into concurrent work streams.
@@ -16,13 +26,15 @@ roadmap:
16
26
  # Example: m1-1-foundation/, m1-2-auth/, m2-1-dashboard/
17
27
  phases:
18
28
  - id: 1
19
- name: "" # e.g., "Foundation & Architecture"
20
- goal: "" # Outcome, not task. "Users can X" not "Build Y"
21
- requirements: [] # List of FR-IDs this phase delivers
29
+ name: "" # Vertical slice name, e.g., "User can sign up"
30
+ goal: "" # User-observable outcome. "User can X". Never "Build Y".
31
+ slice_exception: null # null | infra_bootstrap | shared_library | data_migration
32
+ # Only set if this phase legitimately has no user-facing surface.
33
+ requirements: [] # List of FR-IDs this phase delivers (end-to-end)
22
34
  dependencies: [] # Phase IDs that must complete first
23
- success_criteria: # Observable truths when phase is done
24
- - "" # e.g., "User can see the landing page"
25
- - "" # e.g., "API returns valid JSON for /users"
35
+ success_criteria: # User-observable truths when phase is done
36
+ - "" # e.g., "User submits signup form and lands on dashboard"
37
+ - "" # e.g., "Invalid email shows inline error"
26
38
  estimated_hours: null
27
39
  status: pending # pending | researching | planning | executing | verifying | deferred | complete
28
40
 
@@ -60,6 +60,9 @@ Auto-detects complexity. Override: "Use Quick/Standard/Full tier."
60
60
  | Systematic debugging | `debugging` | When stuck |
61
61
  | Upgrade Forge files | `upgrading` | On-demand |
62
62
  | Cross-session memory | `beads-integration` | When Beads installed |
63
+ | Multi-agent orchestration (experimental) | `orchestrating` | Full (opt-in) |
64
+
65
+ > Experimental skills require opt-in install — see `packages/create-forge/experimental/m10/README.md`.
63
66
 
64
67
  ## Context Engineering
65
68
 
@@ -1,37 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Forge claim-check hook prerequisites probe. Informational only — always exit 0.
3
- # Run manually or via install procedure to confirm bash/jq/sqlite3/timeout availability.
4
-
5
- set -uo pipefail
6
-
7
- check() {
8
- local name=$1 cmd=$2 version_flag=${3:---version}
9
- if command -v "$cmd" >/dev/null 2>&1; then
10
- local ver
11
- ver=$("$cmd" "$version_flag" 2>&1 | head -1)
12
- printf ' ✓ %-10s %s\n' "$name" "$ver"
13
- else
14
- printf ' ✗ %-10s MISSING\n' "$name"
15
- fi
16
- }
17
-
18
- echo "Forge claim-check hook — prerequisites"
19
- echo
20
-
21
- check "bash" "bash" "--version"
22
- check "jq" "jq" "--version"
23
- check "sqlite3" "sqlite3" "--version"
24
-
25
- if command -v timeout >/dev/null 2>&1; then
26
- printf ' ✓ %-10s %s\n' "timeout" "$(timeout --version 2>&1 | head -1)"
27
- elif command -v gtimeout >/dev/null 2>&1; then
28
- printf ' ✓ %-10s (gtimeout) %s\n' "timeout" "$(gtimeout --version 2>&1 | head -1)"
29
- else
30
- printf ' ✗ %-10s MISSING (optional — install coreutils for bounded queries)\n' "timeout"
31
- fi
32
-
33
- echo
34
- echo "DB lookup path: ${FORGE_CLAIMS_DB:-${CLAUDE_PROJECT_DIR:-$PWD}/.forge/.mcp-server/claims.db}"
35
- echo "Session id: ${CLAUDE_SESSION_ID:-<unset — hook will fail-open>}"
36
-
37
- exit 0
@@ -1,96 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Forge PreToolUse hook — cross-session file-claim collision detector.
3
- #
4
- # Contract:
5
- # stdin: Claude Code PreToolUse JSON payload
6
- # exit 0 = allow (no claim, own claim, no DB, no session context, unknown schema)
7
- # exit 2 = deny (cross-session claim active, or any internal error — fail-closed)
8
- #
9
- # Defense-in-depth: ERR trap converts ANY unexpected failure into an exit-2 deny.
10
- # Never exit 1 — Claude Code treats exit 1 as soft warning; we want hard block on error.
11
-
12
- set -euo pipefail
13
-
14
- deny() {
15
- echo "[forge-hook] $*" >&2
16
- exit 2
17
- }
18
-
19
- trap 'deny "internal error at line $LINENO — denying for safety (fail-closed)"' ERR
20
-
21
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$PWD}"
22
- DB="${FORGE_CLAIMS_DB:-$PROJECT_DIR/.forge/.mcp-server/claims.db}"
23
- SESSION_ID="${CLAUDE_SESSION_ID:-}"
24
-
25
- # Detect timeout wrapper (macOS lacks GNU `timeout` unless coreutils installed → `gtimeout`).
26
- if command -v timeout >/dev/null 2>&1; then
27
- TIMEOUT_CMD=(timeout 4)
28
- elif command -v gtimeout >/dev/null 2>&1; then
29
- TIMEOUT_CMD=(gtimeout 4)
30
- else
31
- TIMEOUT_CMD=() # no wrapper — sqlite call runs unbounded; busy_timeout in DB still applies
32
- fi
33
-
34
- PAYLOAD=$(cat)
35
-
36
- # Normalize across known path fields:
37
- # Edit / Write → tool_input.file_path
38
- # NotebookEdit → tool_input.notebook_path (validated in spike — see milestone-10-validation.md §2)
39
- # MultiEdit → tool_input.file_path (single file, multiple edits; per docs)
40
- # Future / unknown tools → tool_input.path (defensive)
41
- # jq emits each matching path on its own line. Empty output = no path field present.
42
- FILES=$(printf '%s' "$PAYLOAD" | jq -r '
43
- [ .tool_input.file_path?
44
- , .tool_input.notebook_path?
45
- , .tool_input.path?
46
- , ( .tool_input.edits? // [] | .[]?.file_path? )
47
- ] | map(select(. != null and . != "")) | unique | .[]
48
- ')
49
-
50
- if [ -z "$FILES" ]; then
51
- # No recognized path field — unknown schema. Allow (do not deny on schema drift).
52
- exit 0
53
- fi
54
-
55
- # No DB = MCP server has never run in this repo (fresh-repo case). Fail-open only here.
56
- if [ ! -f "$DB" ]; then
57
- exit 0
58
- fi
59
-
60
- if [ -z "$SESSION_ID" ]; then
61
- # No session context — single-agent or hook invoked outside Claude Code. Allow.
62
- echo "[forge-hook] CLAUDE_SESSION_ID unset — allowing (single-agent mode)" >&2
63
- exit 0
64
- fi
65
-
66
- # Resolve each path to absolute (matches what MCP server stores via path.resolve).
67
- abspath() {
68
- case "$1" in
69
- /*) printf '%s' "$1" ;;
70
- *) printf '%s/%s' "$PROJECT_DIR" "$1" ;;
71
- esac
72
- }
73
-
74
- while IFS= read -r raw; do
75
- [ -z "$raw" ] && continue
76
- file=$(abspath "$raw")
77
-
78
- # Parameterized query via .param — avoids quoting injection on path strings.
79
- result=$("${TIMEOUT_CMD[@]+"${TIMEOUT_CMD[@]}"}" sqlite3 -batch "$DB" \
80
- ".param set :fp '$file'" \
81
- "SELECT session_id || '|' || expires_at FROM claims WHERE file_path = :fp AND expires_at > strftime('%s','now') LIMIT 1;")
82
-
83
- [ -z "$result" ] && continue
84
-
85
- owner="${result%%|*}"
86
- expires_at="${result##*|}"
87
-
88
- if [ "$owner" = "$SESSION_ID" ]; then
89
- continue # own claim
90
- fi
91
-
92
- expires_human=$(date -r "$expires_at" "+%Y-%m-%d %H:%M:%S %Z" 2>/dev/null || echo "epoch:$expires_at")
93
- deny "Edit denied: $file claimed by session $owner until $expires_human. Call forge_release_claims in that session, or wait."
94
- done <<< "$FILES"
95
-
96
- exit 0
@@ -1,135 +0,0 @@
1
- ---
2
- name: orchestrating
3
- description: "[Experimental — M10] Owns multi-agent session lifecycle. Bootstrap, worktree create, claim+merge coordination, teardown. Refuses worktree mode on incompatible repos and falls back to single-agent."
4
- ---
5
-
6
- # Orchestrating
7
-
8
- Multi-agent session lifecycle. Worktree isolation + MCP-coordinated claims + merge queue. Experimental — opt-in per ADR-001. Refuses on incompatible repos, falls back to single-agent.
9
-
10
- ## When to use
11
-
12
- - User explicitly invokes multi-agent mode (`/forge` argument selects multi-agent, or direct skill invocation).
13
- - `executing` skill at Full tier with ≥2 concurrent-eligible phases routes through this skill.
14
-
15
- Skip on Quick tier, single-phase work, or when bootstrap checks fail.
16
-
17
- ## Step 1: Bootstrap
18
-
19
- Run all checks in `bootstrap-checks.md`:
20
-
21
- 1. Git version ≥ 2.48
22
- 2. LFS version ≥ 3.6 (skip if not installed)
23
- 3. No submodules
24
- 4. `core.hooksPath` empty or resolves inside worktree
25
- 5. `git hook run pre-commit` smoke test in fresh worktree
26
-
27
- **Any check fails** → log reason, write `lifecycle.worktree_mode: refused` + `lifecycle.refused_reason` into active milestone state, emit fallback message (see `bootstrap-checks.md`), return to caller. Caller continues single-agent.
28
-
29
- ## Step 2: Session ID + worktree
30
-
31
- ```bash
32
- session_id=$(uuidgen | cut -c1-8)
33
- git worktree prune
34
- git worktree add -b forge/${session_id} --lock --reason "forge session" ../forge-worktrees/${session_id} main
35
- ( cd ../forge-worktrees/${session_id} && git hook run pre-commit || true )
36
- ```
37
-
38
- Verify worktree dir exists, branch locked. On failure → cleanup partial state, refuse mode.
39
-
40
- ## Step 3: State update
41
-
42
- Write into `.forge/state/milestone-{id}.yml`:
43
-
44
- ```yaml
45
- lifecycle:
46
- session_id: "{session_id}"
47
- worktree_path: "../forge-worktrees/{session_id}"
48
- worktree_branch: "forge/{session_id}"
49
- worktree_mode: "active"
50
- started_at: "{ISO8601}"
51
- ```
52
-
53
- Update `state/index.yml` milestone `last_updated`.
54
-
55
- ## Step 4: Hand back
56
-
57
- Return control to caller (typically `executing`). All subsequent work runs inside `../forge-worktrees/{session_id}`. Caller honors claim convention.
58
-
59
- ### Claim convention (executing-skill contract)
60
-
61
- Before any `Edit`, `Write`, `MultiEdit`, or `NotebookEdit` on a file outside `.forge/state/milestone-{own_id}.yml`:
62
-
63
- 1. Call `forge_claim_files` with `{ session_id, files: [...], ttl_seconds: 900 }`.
64
- 2. On `granted` → proceed with edit.
65
- 3. On `conflict: { holder_session, files: [...] }` → surface holder + files to user. Options:
66
- - **wait** → poll `forge_claim_status` until released or TTL expiry.
67
- - **skip** → drop the conflicted file from the task scope, continue with rest.
68
- - **steal** → only if holder session is provably dead (PreToolUse hook validates). Otherwise refused.
69
- 4. After edit batch → claim auto-extends on continued use; explicit `forge_release_claims` on plan-complete.
70
-
71
- PreToolUse hook (installed by plan-03) enforces this — uncaught violations block at hook level, not skill level.
72
-
73
- ## Step 5: Teardown
74
-
75
- Triggered when caller signals work complete OR user requests teardown.
76
-
77
- ```
78
- forge_queue_commit(branch=forge/{session_id}, base_sha={merge_base})
79
- ```
80
-
81
- Branch on response status:
82
-
83
- - **`merged`** → `forge_release_claims(session_id)` → `git worktree remove --force ../forge-worktrees/{session_id}` → `git branch -d forge/{session_id}` → clear `lifecycle.*` (set `worktree_mode: complete`, retain `session_id` for audit).
84
- - **`conflict`** → invoke `Skill(debugging)` with payload `{ conflicted_files, base_sha, messages, branch }`. Teardown blocks until debugging signals resolution (re-invoke teardown after fix).
85
- - **`stale_base`** → caller rebases worktree branch onto `current_main_sha` from response, retries `forge_queue_commit`. Max 3 retries → escalate to conflict path.
86
-
87
- ## Step 6: Crash recovery (next session start)
88
-
89
- If no clean teardown happened previously:
90
-
91
- 1. `git worktree prune` — drops stale admin dirs.
92
- 2. `git branch --list 'forge/*'` — for each branch with no live worktree, prompt user:
93
- - **resume** → re-attach: `git worktree add ../forge-worktrees/{id} forge/{id}` and restore lifecycle state.
94
- - **delete** → `git branch -D forge/{id}`.
95
- 3. MCP server startup handles pidfile takeover + claim TTL expiry independently (see ADR-003).
96
-
97
- ## Failure modes & operator notes
98
-
99
- - **MCP server absent** — bootstrap check 5 (hook smoke) will not detect this. Skill detects on first `forge_claim_files` call: error `MCP_SERVER_UNAVAILABLE` → write `lifecycle.worktree_mode: degraded`, warn user, continue without claim coordination. Hard isolation (worktree) still active; coordination is downgraded to best-effort.
100
- - **Disk full during worktree add** — `git worktree add` will error. Cleanup any partial `../forge-worktrees/{session_id}/` dir, refuse mode, fall back.
101
- - **Concurrent orchestrating invocations** — second invocation reads first's `lifecycle.session_id` in state. If present and `worktree_mode: active` → refuse (one orchestration per milestone). Use a separate milestone for parallel orchestrated work.
102
- - **Worktree path collision** — UUIDv4 short (8 chars) collision negligible at <100 concurrent sessions. If `../forge-worktrees/{id}/` already exists → regenerate session_id, retry up to 3 times.
103
-
104
- ## Example: clean session
105
-
106
- ```
107
- user: /forge multi-agent
108
- forge → orchestrating
109
- orchestrating: bootstrap OK → session_id=a1b2c3d4 → worktree created → state written
110
- orchestrating → executing (working dir: ../forge-worktrees/a1b2c3d4)
111
- executing: claim files → edit → commit (× N tasks)
112
- executing → verifying → reviewing (all inside worktree)
113
- reviewing → orchestrating (teardown)
114
- orchestrating: forge_queue_commit → merged → release claims → remove worktree → delete branch
115
- done.
116
- ```
117
-
118
- ## Example: conflict path
119
-
120
- ```
121
- orchestrating: forge_queue_commit → conflict { files: [src/auth.ts] }
122
- orchestrating → debugging { conflicted_files, base_sha, branch }
123
- debugging: user resolves → signal resolved
124
- orchestrating: retry forge_queue_commit → merged → cleanup
125
- ```
126
-
127
- ## References
128
-
129
- - ADR-001 — experimental track / opt-in carve-out
130
- - ADR-002 — worktrees as isolation substrate
131
- - ADR-003 — MCP server + per-repo SQLite
132
- - ADR-004 — merge queue (forge_queue_commit status semantics)
133
- - ADR-005 — session lifecycle (this skill realizes it)
134
- - `.forge/research/milestone-10.md` — spike findings
135
- - `bootstrap-checks.md` — bootstrap check matrix + fallback
@@ -1,48 +0,0 @@
1
- # Bootstrap Checks
2
-
3
- Run before worktree creation. Any failure → refuse worktree mode, fall back to single-agent.
4
-
5
- | Check | Command | Pass criterion | Fail action | Reason |
6
- |-------|---------|----------------|-------------|--------|
7
- | Git version | `git --version` | major.minor ≥ 2.48 | refuse worktree mode | Known worktree bugs < 2.48 (admin-dir leaks, prune races) |
8
- | LFS version | `git lfs version` (skip if not installed) | ≥ 3.6 OR not installed | refuse worktree mode | Known worktree-locking bugs < 3.6 |
9
- | Submodule scan | `git submodule status` | empty output | refuse worktree mode | Submodules officially "not recommended" in worktrees (git docs) |
10
- | `core.hooksPath` | `git config core.hooksPath` | empty OR path resolves inside worktree | refuse worktree mode | Husky/lefthook silent-skip risk when path points outside worktree |
11
- | Hook smoke test | `git hook run pre-commit` in fresh worktree | exit 0 OR matches main-repo exit code | refuse worktree mode | Confirms hook plumbing actually fires inside worktree |
12
-
13
- ## Fallback message
14
-
15
- On any check failure, emit verbatim to user:
16
-
17
- ```
18
- M10 worktree mode unavailable: <reason>. Continuing in single-agent mode. See ADR-005 for compatibility matrix.
19
- ```
20
-
21
- Replace `<reason>` with the failing check name + observed value (e.g., "Git version 2.45.1 < required 2.48").
22
-
23
- ## State write on refusal
24
-
25
- ```yaml
26
- lifecycle:
27
- worktree_mode: refused
28
- refused_reason: "<check name>: <observed>"
29
- refused_at: "{ISO8601}"
30
- ```
31
-
32
- ## Check execution order
33
-
34
- Run checks 1→5 in order. First failure halts the sequence — no point running hook smoke if Git itself is too old. Record the failing check name + observed value as `refused_reason` so the user knows exactly what to fix.
35
-
36
- ## Remediation hints
37
-
38
- | Failing check | User remediation |
39
- |---------------|------------------|
40
- | Git version | Upgrade Git to ≥ 2.48 (Homebrew: `brew upgrade git`; apt: backports or PPA) |
41
- | LFS version | Upgrade Git LFS to ≥ 3.6 OR uninstall if not actually used |
42
- | Submodule scan | Convert submodules to subtrees or vendored copies; M10 cannot coexist with submodules per upstream git guidance |
43
- | `core.hooksPath` | Either unset (`git config --unset core.hooksPath`) or move hook dir inside repo tree so it resolves under each worktree |
44
- | Hook smoke test | Inspect failing hook output; common cause is hook script assuming `$PWD` is main repo root — fix to use `git rev-parse --show-toplevel` |
45
-
46
- ## Re-running checks
47
-
48
- Bootstrap is idempotent and cheap (~200ms total). Skill re-runs on every session start; users do not invoke directly. If a check transiently fails (e.g., LFS not yet installed during initial setup), simply restart the orchestration entry.