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