forge-orkes 0.41.0 → 0.44.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/bin/create-forge.js +245 -43
- package/package.json +5 -1
- package/template/.claude/agents/doc-reviewer.md +115 -0
- package/template/.claude/agents/performance-reviewer.md +138 -0
- package/template/.claude/agents/security-reviewer.md +163 -0
- package/template/.claude/agents/tester.md +3 -5
- package/template/.claude/hooks/README.md +39 -0
- package/template/.claude/hooks/block-dangerous-commands.sh +158 -0
- package/template/.claude/hooks/forge-active-skill-guard.sh +44 -0
- package/template/.claude/hooks/format-on-save.sh +95 -0
- package/template/.claude/hooks/protect-files.sh +101 -0
- package/template/.claude/hooks/scan-secrets.sh +87 -0
- package/template/.claude/hooks/tests/README.md +76 -0
- package/template/.claude/hooks/tests/cases/block-dangerous-commands.cases.json +376 -0
- package/template/.claude/hooks/tests/cases/protect-files.cases.json +222 -0
- package/template/.claude/hooks/tests/cases/scan-secrets.cases.json +218 -0
- package/template/.claude/hooks/tests/cases/warn-large-files.cases.json +146 -0
- package/template/.claude/hooks/tests/run.sh +118 -0
- package/template/.claude/hooks/warn-large-files.sh +71 -0
- package/template/.claude/rules/README.md +63 -0
- package/template/.claude/rules/agent-discipline.md +14 -0
- package/template/.claude/settings.json +69 -1
- package/template/.claude/skills/architecting/SKILL.md +2 -0
- package/template/.claude/skills/chief-of-staff/SKILL.md +37 -26
- package/template/.claude/skills/executing/SKILL.md +16 -0
- package/template/.claude/skills/forge/SKILL.md +15 -27
- package/template/.claude/skills/forge/desire-paths-review.md +37 -0
- package/template/.claude/skills/initializing/SKILL.md +4 -0
- package/template/.claude/skills/planning/SKILL.md +17 -3
- package/template/.claude/skills/reviewing/SKILL.md +2 -0
- package/template/.claude/skills/testing/SKILL.md +3 -3
- package/template/.claude/skills/verifying/SKILL.md +20 -9
- package/template/.forge/FORGE.md +36 -8
- package/template/.forge/migrations/0.20.0-nested-phase-layout.md +10 -2
- package/template/.forge/migrations/0.42.0-id-reservation.md +48 -0
- package/template/.forge/migrations/0.43.0-safety-policy.md +60 -0
- package/template/.forge/migrations/0.44.0-desire-paths.md +57 -0
- package/template/.forge/reservations.yml +31 -0
package/template/.forge/FORGE.md
CHANGED
|
@@ -44,9 +44,9 @@ Use ordinary Quick/Standard/Full tiers inside a stream. Use Project Chief / Chie
|
|
|
44
44
|
Integration is **event-driven, not polled**: a stream publishes its work to main at plan-marked phase boundaries, and a lightweight flag tells sibling worktrees there is something to pull. No flag set → boot does no integration work.
|
|
45
45
|
|
|
46
46
|
- **Checkpoints (plan-marked).** `planning` marks selected phases as integration checkpoints (`integration_checkpoint: true` in the plan frontmatter) — the points where a verified slice should land on main. Default heuristic: the last phase, plus any phase another stream/phase depends on. Advisory — planning proposes, the operator confirms. **The mark is the opt-in:** a plan with no marked checkpoint never auto-publishes, so existing/unmarked work is unaffected.
|
|
47
|
-
- **Producer — publish on verified checkpoint (fast-forward only).** When `verifying` returns PASS for a checkpoint phase *and the session is a worktree*, it integrates that phase to main with a **fast-forward-only** push: `git push origin HEAD:main`. Git rejects a non-ff push — that *is
|
|
47
|
+
- **Producer — publish on verified checkpoint (fast-forward only, strand-guarded).** When `verifying` returns PASS for a checkpoint phase *and the session is a worktree*, it integrates that phase to main with a **fast-forward-only** push: `git push origin HEAD:main`. Git rejects a non-ff push (vs `origin/main`) — that surfaces a diverged *origin*, never force-pushed. But ff-vs-origin is not enough on its own: `origin/main` is not the operator's canonical checkout, and the local `main` ref can carry commits origin never saw (another stream's merge, a framework bump, state-sync commits). Pushing past a *behind* origin then leaves **local main diverged, not behind** — and the naive "you're behind, `git pull --ff-only`" advice *fails* on divergence (forge#13). So the producer **strand-guards** first: it publishes only when local `main` is an **ancestor of the worktree HEAD** (no local commits this checkpoint omits). Stranded (local main has such commits) → it does **not** push; it surfaces "integrate through local main first" and leaves the work on the branch. On a strand-safe success it sets the **integration flag**. (No remote → fast-forward local `main` where possible, else record ready-to-ff for the primary checkout.)
|
|
48
48
|
- **Flag (cheap, set by the producer).** A marker in the shared git common dir — `.git/forge/integration-pending` — records the new main sha, the publishing stream, and the shared surfaces that changed. It is shared across all of a repo's worktrees on a machine, so any boot reads it for free; absent or already-seen → skip. The `main` ref is the source of truth the marker points at. Cross-machine (the marker is machine-local) falls back to the opt-in `forge.worktree_rebase_check` / a periodic fetch — eventual, not instant.
|
|
49
|
-
- **Consumer — auto fast-forward, else prompt.** `forge` boot in a worktree
|
|
49
|
+
- **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.
|
|
50
50
|
|
|
51
51
|
### Worktree Convention (base)
|
|
52
52
|
|
|
@@ -125,14 +125,15 @@ Auto-detects complexity. Override: "Use Quick/Standard/Full tier."
|
|
|
125
125
|
| `streams/{stream}.yml` | 15 KB | Stream state stays resumable without becoming history |
|
|
126
126
|
| `streams/{stream}/brief.md` | 8 KB | Stream context packet must fit into active session context |
|
|
127
127
|
| `streams/{stream}/packages/{id}.yml` | 10 KB | Delegated work contract stays bounded |
|
|
128
|
+
| `reservations.yml` | 50 KB | Append-only ID-reservation registry; one short line per ADR/DEF/FR/NFR reserved |
|
|
128
129
|
|
|
129
130
|
### Accumulating Artifacts
|
|
130
131
|
|
|
131
132
|
Every new accumulating artifact needs: owner, size gate, archive/compaction rule,
|
|
132
133
|
`derived` vs source-of-truth status, and migration behavior. Defaults:
|
|
133
134
|
|
|
134
|
-
- `roadmap.yml`, `requirements/*.yml`, `releases.yml` are
|
|
135
|
-
scoped slices, not whole history.
|
|
135
|
+
- `roadmap.yml`, `requirements/*.yml`, `releases.yml`, `reservations.yml` are
|
|
136
|
+
source-of-truth; load scoped slices, not whole history.
|
|
136
137
|
- `phases/`, `audits/`, `research/`, `archive/`, and `context-archive.md` are
|
|
137
138
|
historical; load only by milestone/stream/package/version.
|
|
138
139
|
- Runtime/generated files (`.mcp-server/*.db*`, logs, spike DBs) are excluded
|
|
@@ -150,6 +151,14 @@ Skills load only when invoked. Active context is `.forge/context.md`; consult
|
|
|
150
151
|
`.forge/context-archive.md` only when older decisions matter. Skill details stay
|
|
151
152
|
on demand. Base context ~300 lines.
|
|
152
153
|
|
|
154
|
+
### Rules Layer (`.claude/rules/`)
|
|
155
|
+
Three policy weights, lightest-first (ADR-018):
|
|
156
|
+
- **`constitution.md`** — always-loaded architectural **gates** (heavy, project-wide, hard rules; size-gated 10 KB).
|
|
157
|
+
- **`.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**).
|
|
158
|
+
- **Skills** — heavy, opt-in, loaded by task description.
|
|
159
|
+
|
|
160
|
+
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`.
|
|
161
|
+
|
|
153
162
|
## Model Routing
|
|
154
163
|
|
|
155
164
|
Configure in `project.yml`:
|
|
@@ -178,8 +187,13 @@ models:
|
|
|
178
187
|
| `planner` | Planning + constitutional gates | Read + Write (plan files) | Planning |
|
|
179
188
|
| `executor` | Building + deviation rules | All dev tools | Execution |
|
|
180
189
|
| `verifier` | Goal-backward verification | Read + Bash (tests) | Verification |
|
|
181
|
-
| `reviewer` | Security + architecture + refactoring | Read, Bash, Grep, Glob | Reviewing |
|
|
190
|
+
| `reviewer` | Security + architecture + refactoring (breadth pass, gates the health report) | Read, Bash, Grep, Glob | Reviewing |
|
|
182
191
|
| `tester` | E2E/integration authoring + suite analysis | Dual-mode: author (Write/Edit/Bash/git) · analyst (Read/Glob/Grep/Bash read-only) | Testing skill |
|
|
192
|
+
| `security-reviewer` | Confidence-gated security depth (≥8, exploit-scenario required) | Read, Grep, Glob, Bash | Optional deep-dive (from `reviewing` or direct) |
|
|
193
|
+
| `performance-reviewer` | Real bottlenecks only (Impact = frequency × cost; ≥8 + ≥Medium) | Read, Grep, Glob, Bash | Optional deep-dive |
|
|
194
|
+
| `doc-reviewer` | Docs-vs-source accuracy (≥8; Blocker/Major/Minor) | Read, Grep, Glob, Bash | Optional deep-dive |
|
|
195
|
+
|
|
196
|
+
The three specialist reviewers **complement** the generalist `reviewer` — single-axis, defensible depth, read-only. They are pulled in deliberately (the `reviewing` skill can spawn them, or invoke directly); the 3-mode `reviewer` remains the breadth pass that gates the milestone health report.
|
|
183
197
|
|
|
184
198
|
## Project Init (First Run)
|
|
185
199
|
|
|
@@ -197,7 +211,7 @@ State lives in `.forge/`:
|
|
|
197
211
|
- `project.yml` — Vision, stack, design system, verification, constraints (<5KB)
|
|
198
212
|
- `constitution.md` — Active architectural gates
|
|
199
213
|
- `design-system.md` — Component mapping table
|
|
200
|
-
- `requirements/m{N}.yml` — Per-milestone structured requirements with `[NEEDS CLARIFICATION]` markers. **FR-IDs, DEF-IDs, and NFR-IDs are globally unique across all milestone files** — `FR-001` may exist in exactly one `m{N}.yml`. Before adding a new ID,
|
|
214
|
+
- `requirements/m{N}.yml` — Per-milestone structured requirements with `[NEEDS CLARIFICATION]` markers. **FR-IDs, DEF-IDs, and NFR-IDs are globally unique across all milestone files** — `FR-001` may exist in exactly one `m{N}.yml`. Before adding a new ID, **reserve it via the ID Reservation Protocol** (append to `.forge/reservations.yml`, commit+push, then write) — the next number is `max(highest reserved, highest in `.forge/requirements/*.yml`) + 1`. That reservation is what makes the shared ID space safe across **concurrent worktrees**: bare scan-and-increment sees only committed state, so two worktrees branched from one baseline claim the same number and collide silently until merge (see ADR-016). On a residual collision (legacy/un-reserved, e.g. during a migration), keep the older milestone's ID and renumber the newer. Concurrent milestones each own their file — no cross-stream contention on file writes, but ID space is shared. Functional requirements may carry M9 e2e gate fields (`e2e`, `observable_outcome`, `observable_outcome_hash`, `validated`) — lazy migration, absent fields default to `e2e:false`/`validated:false`.
|
|
201
215
|
- `roadmap.yml` — Phases, milestones, dependencies
|
|
202
216
|
- `state/index.yml` — DERIVED registry rolled up from milestone files (id, name, status, last_updated). Never hand-edited; never written by worktree agents.
|
|
203
217
|
- `state/milestone-{id}.yml` — Per-milestone cursor (single source of truth): position, progress, decisions, blockers. One owner at a time.
|
|
@@ -213,13 +227,14 @@ State lives in `.forge/`:
|
|
|
213
227
|
- `refactor-backlog.yml` — Refactoring catalog (actionable items only), worked via quick-tasking. Canonical status vocab: `pending | in_progress | done | dismissed | deferred`. Terminal items auto-archive on the next reviewing/quick-tasking write (compaction-on-write).
|
|
214
228
|
- `refactor-backlog-archive.yml` — Append-only terminal-item archive (`done`/`dismissed`); keeps the audit trail out of the working backlog.
|
|
215
229
|
- `releases.yml` — Append-only version-reservation registry (cross-session coordination point; see Version Reservation Protocol)
|
|
230
|
+
- `reservations.yml` — Append-only ID-reservation registry for ADR/DEF/FR/NFR numbers (cross-worktree coordination point; see ID Reservation Protocol)
|
|
216
231
|
- `archive/milestone-{id}/` — Archived milestone artifacts (archive-delete)
|
|
217
232
|
|
|
218
233
|
**Milestones** group phases into concurrent streams. Own state file — no conflicts across sessions. Can be deferred (frozen in place) or archive-deleted.
|
|
219
234
|
`index.yml status` gates routing: `not_started | active | deferred | complete`.
|
|
220
235
|
|
|
221
236
|
**Stream Rollup (`active.yml` is derived).** Mirrors the milestone Rollup: `active.yml` is a pure projection, regenerated — never hand-edited — by a deterministic, idempotent **join** of two single-sourced inputs (ADR-015). Run by the main/orchestrator session and at `chief-of-staff` Show/Sync; worktrees never write it. Procedure:
|
|
222
|
-
1. Glob `streams/{stream}.yml`. Each → a row carrying its coordination state (`coordination` = the file's `status`), goal, runtime, ownership summary, `merge.readiness
|
|
237
|
+
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.
|
|
223
238
|
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.
|
|
224
239
|
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.
|
|
225
240
|
4. Derive `merge_queue:` — one entry per stream with `merge.readiness: ready`.
|
|
@@ -248,8 +263,9 @@ with the work it describes. Every artifact has a sharing class:
|
|
|
248
263
|
| Class | Files | Write rule |
|
|
249
264
|
|---|---|---|
|
|
250
265
|
| **Single-owner mutable** | `state/milestone-{id}.yml`, `phases/milestone-{id}/`, `research/milestone-{id}.md` | Exactly one writer at a time — the worktree (or main session) currently driving that milestone. **Other worktrees never touch it**, not even to read-then-write — they pull from main if they need it. |
|
|
266
|
+
| **Single-owner mutable (per stream)** | `streams/{stream}.yml`, `streams/{stream}/brief.md`, `streams/{stream}/packages/*.yml` | The stream's **current driver** writes them — one writer at a time, like the milestone file. Distinct streams own distinct files (no cross-stream contention). `active.yml` is **derived** from them: a worktree driving a stream may write its own stream file but **never** `active.yml` or a peer stream's file. |
|
|
251
267
|
| **Derived** | `state/index.yml`, `streams/active.yml` | Never hand-edited. Regenerated by rollup from their sources (`index.yml` ← `milestone-*.yml` via Rollup 1.0; `active.yml` ← per-stream files + active milestones via the Stream Rollup). Only the main/orchestrator session (and `chief-of-staff` for `active.yml`) runs rollup. |
|
|
252
|
-
| **Append-only shared** | `releases.yml` (version reservations), `state/desire-paths/*` (one file per observation) | Many writers OK; structure prevents collision. `releases.yml`
|
|
268
|
+
| **Append-only shared** | `releases.yml` (version reservations), `reservations.yml` (ADR/DEF/FR/NFR id reservations), `state/desire-paths/*` (one file per observation) | Many writers OK; structure prevents collision. `releases.yml` + `reservations.yml` use the reservation protocols (append + commit + push before depending on the number); desire-paths use distinct filenames so two writers never touch the same file. |
|
|
253
269
|
| **Shared mutable** | `context.md`, `refactor-backlog.yml`, `requirements/m{N}.yml` (ID space `FR-`/`DEF-`/`NFR-` is globally shared even though each milestone owns its file) | Write only to your milestone/stream block. Within a block, append-only; strike through instead of rewriting. |
|
|
254
270
|
| **Stable shared** | `project.yml`, `constitution.md`, `design-system.md`, `roadmap.yml`, `FORGE.md`, `.claude/skills/`, `.claude/agents/` | Rarely changes except via `/upgrading`. Canonical on main; worktrees lag until they rebase. An upgrade run touches **only the checkout it runs in**, so after upgrading in main, live worktrees keep their branch-point framework files — `upgrading` **Step 8** enumerates live `forge/m-*` worktrees and offers to rebase each (never auto), and the opt-in `forge.worktree_rebase_check` watches these files as a boot-time safety net. Gitignored carve-outs (`.mcp.json`, experimental hooks) don't rebase — re-run the experimental installer if the upgrade changed them. |
|
|
255
271
|
|
|
@@ -282,6 +298,18 @@ The project version + CHANGELOG slot are **shared resources** — when two miles
|
|
|
282
298
|
|
|
283
299
|
**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.
|
|
284
300
|
|
|
301
|
+
### ID Reservation Protocol
|
|
302
|
+
|
|
303
|
+
Sequential IDs — `ADR-NNN` (decision records), `DEF-NNN` (deferred issues), `FR-NNN`/`NFR-NNN` (requirements) — are **shared cross-worktree resources** the same way the version number is. "Scan the tree for the highest and increment" only sees *committed* state, so two streams branched from the same baseline each claim the same next number and collide **silently until merge** — a costly multi-file renumber pass (see [ADR-016](../docs/decisions/ADR-016-id-reservation-protocol.md)). `.forge/reservations.yml` removes the number from per-stream scope, exactly as `releases.yml` does for versions.
|
|
304
|
+
|
|
305
|
+
- **Reserve before allocating.** Before authoring a new ADR or writing a new DEF/FR/NFR ID, append ONE entry to `.forge/reservations.yml` — `{kind, id, milestone, reserved_at, summary}`, `kind ∈ {adr, def, fr, nfr}` — then **commit + push it BEFORE creating the artifact**. A parallel worktree pulls and sees the number taken.
|
|
306
|
+
- **Next number = `max(reserved, in-tree) + 1` per kind.** The higher of (a) the highest reservation for that kind in `reservations.yml` and (b) the highest ID of that kind actually landed in the tree. Taking both keeps it backward-compatible — landed IDs that were never reserved are still respected.
|
|
307
|
+
- **Append-only — never edit prior entries.** Distinct lines = no contention. Race resolution: the second of two near-simultaneous reservations rebases onto the first (append-only → clean), re-reads `max()`, takes the next number.
|
|
308
|
+
- **Reserve points.** `architecting` reserves an `adr` before authoring it; `planning` reserves `fr`/`nfr`/`def` before writing them into requirements. (Out of scope: `executing`'s `DI-NNN` deferred-*issue* ids in `deferred-issues.md` are a separate, more local namespace — not reserved here.)
|
|
309
|
+
- **Lazy migration.** No `reservations.yml` ⇒ allocate as before (scan in-tree max); the first reservation creates the file. Existing projects are unaffected until they next allocate an ID.
|
|
310
|
+
|
|
311
|
+
This is the cross-worktree mechanism the FR/DEF/NFR "globally unique" rule (State Management, above) previously lacked, and the first reservation rule ADR numbers have ever had. Versions stay in `releases.yml` (separate semver/CHANGELOG semantics); everything else reserves here.
|
|
312
|
+
|
|
285
313
|
## Deviation Rules
|
|
286
314
|
|
|
287
315
|
**Full definitions:** `.claude/agents/executor.md`.
|
|
@@ -119,10 +119,18 @@ Use `git mv` so history follows the files and `git status` records a rename
|
|
|
119
119
|
|
|
120
120
|
```bash
|
|
121
121
|
cd .forge/phases
|
|
122
|
+
# POSIX parameter expansion — bash- AND zsh-safe. Do NOT use [[ =~ ]] + $BASH_REMATCH
|
|
123
|
+
# here: under zsh (macOS default, and the shell quick-tasking runs this in) captures
|
|
124
|
+
# land in $match, not $BASH_REMATCH, so id/phase/name expand empty and git mv
|
|
125
|
+
# collapses every phase dir onto a malformed milestone-/- target.
|
|
122
126
|
for d in m[0-9]*-[0-9]*-*/; do
|
|
123
127
|
d=${d%/}
|
|
124
|
-
|
|
125
|
-
id=${
|
|
128
|
+
rest=${d#m} # strip leading 'm' → id-phase-name
|
|
129
|
+
id=${rest%%-*} # digits before first '-'
|
|
130
|
+
rest=${rest#*-} # drop 'id-' → phase-name
|
|
131
|
+
phase=${rest%%-*} # digits before next '-'
|
|
132
|
+
name=${rest#*-} # remainder (may contain '-')
|
|
133
|
+
case "$id$phase" in *[!0-9]*) continue;; esac # skip if id/phase non-numeric
|
|
126
134
|
mkdir -p "milestone-$id"
|
|
127
135
|
git mv "$d" "milestone-$id/$phase-$name"
|
|
128
136
|
done
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Migration Guide: ID Reservation Protocol (Forge 0.42.0)
|
|
2
|
+
|
|
3
|
+
Forge 0.42.0 ([ADR-016](https://github.com/Attuned-Media/forge/blob/main/docs/decisions/ADR-016-id-reservation-protocol.md), [forge#10](https://github.com/Attuned-Media/forge/issues/10)) adds a cross-worktree reservation mechanism for sequential IDs — `ADR-NNN`, `DEF-NNN`, `FR-NNN`, `NFR-NNN` — backed by a new append-only file `.forge/reservations.yml` (sibling to `releases.yml`). It closes the silent collision where two streams in parallel worktrees each "scan the tree for the highest + increment" and claim the same number, surfacing only at merge as a multi-file renumber.
|
|
4
|
+
|
|
5
|
+
**Fully backward-compatible / lazy.** Nothing breaks without acting: with no `reservations.yml`, allocation behaves exactly as before (scan the in-tree max), and the first reservation creates the file. This guide just materialises the file so the protocol is active and visible immediately.
|
|
6
|
+
|
|
7
|
+
## Detection
|
|
8
|
+
|
|
9
|
+
Prints `MIGRATE` when the project has no `.forge/reservations.yml` yet. Silent + exit 0 once it exists.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
[ -d .forge ] || exit 0 # not a Forge project
|
|
13
|
+
[ -f .forge/reservations.yml ] && exit 0 # already migrated → silent
|
|
14
|
+
echo "MIGRATE — add .forge/reservations.yml to activate the ID Reservation Protocol (ADR/DEF/FR/NFR cross-worktree number reservation)"
|
|
15
|
+
exit 0
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Migration steps
|
|
19
|
+
|
|
20
|
+
One trivial, safe step — create the starter file. In Claude Code this runs via `quick-tasking`.
|
|
21
|
+
|
|
22
|
+
### 1. Create `.forge/reservations.yml`
|
|
23
|
+
|
|
24
|
+
Write the append-only starter (header + empty list). It needs no back-fill — the `max(reserved, in-tree)` allocation rule already respects every ADR/DEF/FR/NFR already landed in the tree, so existing IDs stay valid without being listed.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
cat > .forge/reservations.yml <<'YML'
|
|
28
|
+
# Forge ID Reservations — cross-worktree coordination for ADR / DEF / FR / NFR numbers
|
|
29
|
+
# Reserve BEFORE allocating: append one entry, commit + push, THEN create the artifact.
|
|
30
|
+
# Next number for a kind = max(highest reserved here, highest in-tree) + 1. kind ∈ {adr,def,fr,nfr}.
|
|
31
|
+
# Append-only; never edit prior entries. See FORGE.md → ID Reservation Protocol + ADR-016.
|
|
32
|
+
reservations: []
|
|
33
|
+
YML
|
|
34
|
+
git add .forge/reservations.yml
|
|
35
|
+
git commit -m "chore(forge): add reservations.yml (ID Reservation Protocol, 0.42.0)"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 2. From now on, reserve before allocating
|
|
39
|
+
|
|
40
|
+
When `architecting` files a new ADR or `planning` writes a new FR/NFR/DEF, append `{kind, id, milestone, reserved_at, summary}` to `.forge/reservations.yml`, commit + push it, *then* create the artifact. The skills (0.42.0) prompt this automatically.
|
|
41
|
+
|
|
42
|
+
## Validation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
[ -f .forge/reservations.yml ] && echo "OK: reservations.yml present" || echo "FAIL: missing"
|
|
46
|
+
grep -q '^reservations:' .forge/reservations.yml && echo "OK: has reservations key" || echo "FAIL: malformed"
|
|
47
|
+
echo "validation complete"
|
|
48
|
+
```
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Migration Guide: Safety & Policy Hardening (Forge 0.43.0)
|
|
2
|
+
|
|
3
|
+
Forge 0.43.0 adds two layers ported from a comparison against the Sendmarc internal framework:
|
|
4
|
+
|
|
5
|
+
- **[ADR-017](https://github.com/Attuned-Media/forge/blob/main/docs/decisions/ADR-017-deterministic-safety-guardrails.md)** — deterministic safety-guardrail hooks (`block-dangerous-commands`, `scan-secrets`, `protect-files`, `warn-large-files`), `format-on-save`, `permissions.deny` for secrets, and a hook test harness.
|
|
6
|
+
- **[ADR-018](https://github.com/Attuned-Media/forge/blob/main/docs/decisions/ADR-018-lightweight-policy-layer.md)** — the `.claude/rules/` layer (always-on `agent-discipline` rule), confidence-gated specialist reviewer agents, and an opt-in `experimental/conventions/` pack.
|
|
7
|
+
|
|
8
|
+
**Mostly automatic, backward-compatible.** Running `npx forge-orkes@latest upgrade` does the heavy lifting: it additively installs the guardrail hooks and the always-on rule, syncs the specialist agents, and merges the hook wiring + `deny` globs into your `settings.json` — **without** removing any hooks/rules you added (m10's `forge-claim-check` and pack-installed stack rules are preserved). Nothing breaks if you do nothing; the guardrails simply aren't active until the settings carry them.
|
|
9
|
+
|
|
10
|
+
## Detection
|
|
11
|
+
|
|
12
|
+
Prints `MIGRATE` when the project is missing the safety hooks. Silent + exit 0 once present.
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
[ -d .forge ] || exit 0 # not a Forge project
|
|
16
|
+
[ -f .claude/hooks/block-dangerous-commands.sh ] && exit 0 # already migrated → silent
|
|
17
|
+
echo "MIGRATE — run 'npx forge-orkes@latest upgrade' to install the safety guardrails (ADR-017) + rules layer (ADR-018)"
|
|
18
|
+
exit 0
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Migration steps
|
|
22
|
+
|
|
23
|
+
### 1. Upgrade
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx forge-orkes@latest upgrade
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This installs/refreshes `.claude/hooks/` (guardrails + `tests/`), adds `.claude/rules/agent-discipline.md`, syncs the `security/performance/doc-reviewer` agents, `chmod +x`'s the hook scripts, and additively merges the hook groups + secret-`deny` globs into `.claude/settings.json` (idempotent — safe to re-run).
|
|
30
|
+
|
|
31
|
+
### 2. Ensure `jq` is installed
|
|
32
|
+
|
|
33
|
+
The guard hooks parse tool JSON with `jq` and **fail closed** (block with an install message) without it. It is already a Forge prerequisite:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
jq --version || brew install jq # macOS
|
|
37
|
+
jq --version || sudo apt install jq # Debian/Ubuntu
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 3. Verify
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
.claude/hooks/tests/run.sh # hook test suite — expect all green
|
|
44
|
+
ls -l .claude/hooks/*.sh # expect -rwxr-xr-x (executable)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
If the scripts aren't executable (a copy can strip the bit): `chmod +x .claude/hooks/*.sh`.
|
|
48
|
+
|
|
49
|
+
## Notes & opt-outs
|
|
50
|
+
|
|
51
|
+
- **Protected-branch pushes are blocked by default** (`main`, `master`, + `git init.defaultBranch`). If a repo legitimately commits to `main`, set `CLAUDE_PROTECTED_BRANCHES=""` (empty) in your shell or `settings.json` `env` to disable *that check only* — force-push/filesystem/db/publish guards stay on. Add extra branches with `CLAUDE_PROTECTED_BRANCHES="main,develop,staging"`.
|
|
52
|
+
- **Editing hooks/settings** is blocked/prompted by `protect-files`. In a project you re-sync hooks via `/upgrading`, not by hand. To author them deliberately, set `FORGE_ALLOW_HOOK_EDITS=1`.
|
|
53
|
+
- **Describing a dangerous command in a commit message** (e.g. the literal text `git push --force`) can trip `block-dangerous-commands`, since it greps the whole Bash command string. Reword, or approve the override.
|
|
54
|
+
- **Opt-in stack conventions:** if your project is Laravel/PHP, Python, or Vue, install the convention skills + path-scoped rules deliberately — they are not in the base template (Forge core is stack-agnostic):
|
|
55
|
+
```bash
|
|
56
|
+
bash experimental/conventions/install.sh # all stacks
|
|
57
|
+
bash experimental/conventions/install.sh laravel # one stack
|
|
58
|
+
```
|
|
59
|
+
(Delivered via the dev repo / `upgrading` experimental route, like m10 — not the npm tarball.)
|
|
60
|
+
- **Specialist reviewers** are optional depth — the `reviewing` skill can spawn them for high-risk diffs, or invoke `security-reviewer` / `performance-reviewer` / `doc-reviewer` directly. The 3-mode `reviewer` is unchanged.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Migration Guide: Desire-Path Hardening (Forge 0.44.0)
|
|
2
|
+
|
|
3
|
+
Forge 0.44.0 batches six desire-path framework fixes (forge#11–#16) surfaced from real multi-worktree usage. **It is backward-compatible** — five of the six are skill/prose behaviour changes that take effect the moment the framework files are synced, with nothing to migrate per project. The only mechanical change is the active-skill PreToolUse hook (forge#12), and `npx forge-orkes upgrade` applies it for you.
|
|
4
|
+
|
|
5
|
+
## What changed (no action needed)
|
|
6
|
+
|
|
7
|
+
- **#11** — the ID Reservation Protocol is now a **blocking gate** in `planning`/`architecting`, plus a new `forge` boot preflight (absent-`reservations.yml` nudge + un-reserved-ID backfill scan). Advisory; surfaces, never blocks.
|
|
8
|
+
- **#13** — checkpoint publish (`verifying`) gains a **strand guard**: it pushes `origin HEAD:main` only when local `main` is an ancestor of the worktree HEAD, so a checkpoint can't leave your canonical main diverged. `forge` boot now also fast-forwards the **primary** main checkout from origin after a checkpoint.
|
|
9
|
+
- **#14** — `executing` Step 0 now inits git submodules for worktrees from **any** origin (not just `orchestrating`-created ones) before the first build.
|
|
10
|
+
- **#15** — `chief-of-staff` gains **Exclusive Resource Leases** (device/port/singleton) and `verifying`'s device gate acquires/releases them.
|
|
11
|
+
- **#16** — `planning` adds a block-anchored **overlay/popover must_have checklist**.
|
|
12
|
+
|
|
13
|
+
These ride in on the skill files synced by the upgrade. No per-project state changes.
|
|
14
|
+
|
|
15
|
+
## The one mechanical change (auto-applied): active-skill hook (#12)
|
|
16
|
+
|
|
17
|
+
The inline PreToolUse(Write\|Edit) "No active skill" guard in `.claude/settings.json` is replaced by `.claude/hooks/forge-active-skill-guard.sh`, which also allows edits when a milestone is durably `current.status: executing` (so a cleared marker no longer blocks a legitimate executing session). On upgrade, `create-forge.js` **strips the legacy inline guard** from your `settings.json` and the additive hooks sync installs + `chmod +x`'s the new script. Idempotent — re-running upgrade is a no-op.
|
|
18
|
+
|
|
19
|
+
## Detection
|
|
20
|
+
|
|
21
|
+
Prints `MIGRATE` only if a project still carries the **legacy inline** active-skill guard in `settings.json` (i.e. the new installer hasn't run yet). Silent + exit 0 once migrated.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
[ -d .forge ] || exit 0 # not a Forge project
|
|
25
|
+
[ -f .claude/settings.json ] || exit 0 # no settings → nothing to migrate
|
|
26
|
+
if grep -q 'if \[ ! -f' .claude/settings.json && grep -q 'No active skill' .claude/settings.json; then
|
|
27
|
+
echo "MIGRATE — settings.json still has the inline active-skill guard; re-run 'npx forge-orkes upgrade' to install forge-active-skill-guard.sh (forge#12)"
|
|
28
|
+
fi
|
|
29
|
+
exit 0
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Manual migration (only if Detection prints MIGRATE)
|
|
33
|
+
|
|
34
|
+
Normally unnecessary — the installer does this. If you hand-merge settings, replace the inline `PreToolUse` "Write|Edit" guard command:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
if [ ! -f "$(git rev-parse --show-toplevel 2>/dev/null)/.forge/.active-skill" ]; then echo "[Forge] No active skill. ..." >&2; exit 2; fi
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
with the script call, and ensure the script is present + executable:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# In .claude/settings.json, the PreToolUse Write|Edit hook command becomes:
|
|
44
|
+
# bash "$CLAUDE_PROJECT_DIR/.claude/hooks/forge-active-skill-guard.sh"
|
|
45
|
+
chmod +x .claude/hooks/forge-active-skill-guard.sh
|
|
46
|
+
git add .claude/settings.json .claude/hooks/forge-active-skill-guard.sh
|
|
47
|
+
git commit -m "chore(forge): migrate active-skill guard to script (0.44.0, forge#12)"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Validation
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
[ -x .claude/hooks/forge-active-skill-guard.sh ] && echo "OK: guard script present + executable" || echo "FAIL: guard script missing/not executable"
|
|
54
|
+
grep -q 'forge-active-skill-guard.sh' .claude/settings.json && echo "OK: settings wired to script" || echo "FAIL: settings not wired"
|
|
55
|
+
grep -q 'if \[ ! -f' .claude/settings.json && echo "WARN: legacy inline guard still present" || echo "OK: legacy inline guard removed"
|
|
56
|
+
echo "validation complete"
|
|
57
|
+
```
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Forge ID Reservations — cross-worktree coordination for ADR / DEF / FR / NFR numbers
|
|
2
|
+
#
|
|
3
|
+
# PURPOSE: remove sequential ID numbers from per-stream scope so two streams
|
|
4
|
+
# running in parallel worktrees never silently claim the same ADR/DEF/FR/NFR
|
|
5
|
+
# number (a collision that otherwise only surfaces at merge as a multi-file
|
|
6
|
+
# renumber). Sibling to releases.yml, which does the same for version numbers.
|
|
7
|
+
# See ADR-016 + FORGE.md → ID Reservation Protocol.
|
|
8
|
+
#
|
|
9
|
+
# COMMITTED + APPEND-ONLY. Reserve BEFORE allocating: append exactly one entry,
|
|
10
|
+
# commit + push it, THEN create the ADR file / write the DEF/FR/NFR. Appending —
|
|
11
|
+
# never editing prior entries — is what keeps parallel worktrees from conflicting
|
|
12
|
+
# (distinct lines, no contention). A worktree pulls and sees taken numbers.
|
|
13
|
+
#
|
|
14
|
+
# RULE: next free number for a kind = max(highest reserved here for that kind,
|
|
15
|
+
# highest actually in-tree for that kind) + 1. Taking the max of both keeps it
|
|
16
|
+
# backward-compatible — landed IDs that predate this file are still respected.
|
|
17
|
+
#
|
|
18
|
+
# RACE: if two worktrees reserve near-simultaneously, the second push rebases
|
|
19
|
+
# onto the first (append-only → clean), re-reads max(), takes the next number.
|
|
20
|
+
#
|
|
21
|
+
# LAZY MIGRATION: absent ⇒ allocate as before (scan in-tree max). This file
|
|
22
|
+
# materialises on the first reservation. kind ∈ {adr, def, fr, nfr}.
|
|
23
|
+
#
|
|
24
|
+
# reservations:
|
|
25
|
+
# - kind: adr
|
|
26
|
+
# id: ADR-036
|
|
27
|
+
# milestone: m-PLUG01
|
|
28
|
+
# reserved_at: "2026-06-26"
|
|
29
|
+
# summary: "Plugin-host scanner architecture"
|
|
30
|
+
|
|
31
|
+
reservations: []
|