forge-orkes 0.36.0 → 0.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forge-orkes",
3
- "version": "0.36.0",
3
+ "version": "0.39.0",
4
4
  "description": "Set up the Forge meta-prompting framework for Claude Code in your project",
5
5
  "bin": {
6
6
  "create-forge": "./bin/create-forge.js"
@@ -41,12 +41,13 @@ questions return to the Chief before work continues.
41
41
 
42
42
  ## Context Loading
43
43
 
44
- 1. Read `.forge/streams/active.yml` first.
45
- 2. If missing, create it from `.forge/templates/streams/active.yml`.
46
- 3. Load only the selected stream's `.forge/streams/{stream}.yml`.
47
- 4. Load the selected stream's `brief.md` when the user needs status, handoff,
44
+ `active.yml` is **derived** (ADR-015) — regenerate it, don't read a possibly-stale copy and never hand-edit it.
45
+
46
+ 1. **Run the Stream Rollup** (below) to regenerate `.forge/streams/active.yml` from the per-stream files + active milestones, then read it. No `streams/` dir and no active milestones → nothing to roll up.
47
+ 2. The per-stream files `.forge/streams/{stream}.yml` are the **source of truth** — load the selected stream's file for detail.
48
+ 3. Load the selected stream's `brief.md` when the user needs status, handoff,
48
49
  conflict context, or resume context.
49
- 5. Load work packages only when delegating, checking worker status, or closing
50
+ 4. Load work packages only when delegating, checking worker status, or closing
50
51
  a stream.
51
52
 
52
53
  Keep summaries in the registry short. Deep history belongs in stream briefs,
@@ -56,54 +57,50 @@ packages, plans, verification reports, commits, and archived artifacts.
56
57
 
57
58
  | Intent | Examples | Action |
58
59
  |--------|----------|--------|
59
- | Show | "show streams", "active streams", "what is running?" | Run Registry Drift Check + Merge Cadence Check, then summarize registry without loading all deep history |
60
+ | Show | "show streams", "active streams", "what is running?" | Run Stream Rollup + Merge Cadence Check, then summarize the derived registry without loading all deep history |
60
61
  | Start | "start stream X", "new stream for billing" | Snapshot current stream, create stream files, register stream |
61
62
  | Pause | "pause stream X" | Update brief, mark paused, keep next action explicit |
62
63
  | Resume | "resume stream X" | Load stream context, set active, surface blockers and ownership |
63
64
  | Delegate | "delegate stream X", "farm this out" | Create or update a work package and handoff prompt |
64
65
  | Adopt M10 | "adopt existing M10 worktrees", "migrate active orchestrating sessions" | Wrap live M10 worktrees as streams without teardown |
65
- | Sync | "sync streams" | Run Registry Drift Check + Merge Cadence Check, then refresh registry summaries from active stream briefs |
66
+ | Sync | "sync streams" | Update per-stream files from briefs, run Stream Rollup + Merge Cadence Check to regenerate the derived registry |
66
67
  | Detect Conflicts | "detect conflicts", "check stream conflicts" | Compare ownership and dependencies |
67
68
  | Merge Safe | "merge safe?", "what can land?" | Identify ready streams with no blocking conflicts |
68
69
  | Close | "close stream X" | Record final status, archive/mark closed, clear active pointer if needed |
69
70
 
70
- ## Registry Drift Check
71
-
72
- Forge keeps two registries that both describe "what work is active," with no
73
- automatic reconciliation: `.forge/state/index.yml` (milestones, owned by the
74
- `forge` skill) and `.forge/streams/active.yml` (streams, owned by the Chief).
75
- `/forge` and `/chief-of-staff` can therefore present different pictures of the
76
- project. Run this check at the **start of the Show and Sync intents** so the
77
- divergence is surfaced instead of silently left to the operator.
78
-
79
- This is **advisory — it never blocks, never auto-creates a milestone, and never
80
- auto-writes a stream.** It reports, and offers.
81
-
82
- 1. Read active milestones from `.forge/state/index.yml` (those with
83
- `status: active`). If the file is absent, skip the check silently — a
84
- streams-only project has nothing to reconcile.
85
- - Note: invoking `/chief-of-staff` directly bypasses `forge`'s boot preflight,
86
- so `index.yml` may be stale (it is regenerated by `forge`'s Rollup, not by
87
- the Chief). Read it as-is; do not run Rollup from the Chief. If it looks
88
- stale, say so and suggest a `/forge` pass.
89
- 2. Read the stream registry from `.forge/streams/active.yml`.
90
- 3. Map each active milestone to a stream by id, trying these forms in order:
91
- `m-{id}`, `milestone-{id}`, bare `{id}`, and any stream whose
92
- `context.milestone` points at `.forge/state/milestone-{id}.yml`.
93
- 4. Report each mismatch:
94
- - **Active milestone with no stream** → offer to add it as a `paused` stream
95
- (do not create it automatically). Quick-fix milestones legitimately may not
96
- warrant a stream let the operator decide.
97
- - **Stream pointing at a `deferred`, `complete`, or absent milestone** → the
98
- stream's status should follow its milestone; offer to update it (pause a
99
- stream whose milestone deferred, close one whose milestone completed).
100
- - **Stream with no milestone counterpart** informational only. Quick-fix
101
- streams are valid; never auto-create a milestone for them.
102
- 5. If everything maps cleanly, say so in one line (`registries aligned`) and
103
- continue to the intent.
104
-
105
- Keep the output compact — a few lines under a `Registry drift:` header, then
106
- proceed with the requested Show/Sync.
71
+ ## Stream Rollup (active.yml is derived)
72
+
73
+ `active.yml` is a **derived projection**, not a hand-maintained registry it is
74
+ regenerated by joining the per-stream files with active milestones (ADR-015,
75
+ FORGE.md Stream Rollup). This **replaces** the old advisory Registry Drift Check:
76
+ drift between the milestone view and the stream view can no longer occur, because
77
+ every field has one source and coverage is computed into the artifact. Run the
78
+ rollup at the **start of Show and Sync**, and after any Start/Resume/Pause/Close
79
+ edit to a stream file.
80
+
81
+ Procedure (deterministic + idempotent never hand-edit `active.yml`):
82
+
83
+ 1. Glob `.forge/streams/{stream}.yml`. Each → a row: `coordination` = the file's
84
+ `status`; plus goal, runtime (branch/worktree), ownership summary,
85
+ `merge_readiness` (from `merge.readiness`), `blocked_by`, `next_action`.
86
+ 2. For a stream with `stream.milestone: m-{id}`, read `phase` + activity from
87
+ `.forge/state/milestone-{id}.yml` (`current.status`, `last_updated`) do not
88
+ store workflow phase in the stream file. Linked milestone `deferred`/`complete`/
89
+ absent `phase` reflects that (and surface it: a deferred milestone's stream
90
+ should likely pause; offer it).
91
+ 3. Glob active milestones (`index.yml` `status: active`). Any with **no** stream
92
+ file → emit a derived `stream: implicit` row (phase from the milestone, no
93
+ coordination) so coverage is guaranteed — an active milestone is never silently
94
+ missing. Offer to materialize a stream file if it needs coordination (never
95
+ auto-create).
96
+ 4. Derive `merge_queue:` one entry per stream with `merge.readiness: ready`.
97
+ 5. Write `active.yml` (`version`, `updated_at`, derived `streams:` sorted by id,
98
+ derived `merge_queue:`). No hand-edited keys.
99
+
100
+ Note: invoked directly, `/chief-of-staff` does not run `forge`'s milestone Rollup,
101
+ so `index.yml` may be slightly stale; read it as-is for step 3 and suggest a
102
+ `/forge` pass if it looks off. The Chief writes `active.yml` (derived class); it
103
+ never writes `index.yml` or any milestone file.
107
104
 
108
105
  ## Start Stream Flow
109
106
 
@@ -117,7 +114,7 @@ proceed with the requested Show/Sync.
117
114
  - `.forge/streams/{id}.yml` from `.forge/templates/streams/stream.yml`
118
115
  - `.forge/streams/{id}/brief.md` from `.forge/templates/streams/brief.md`
119
116
  - `.forge/streams/{id}/packages/`
120
- 5. Add a compact entry to `.forge/streams/active.yml`.
117
+ 5. Set the new stream file's `stream.milestone` link if it backs a milestone (else leave null for a standalone stream), then **regenerate `active.yml` via the Stream Rollup** — do not hand-write a registry entry.
121
118
  6. Declare ownership:
122
119
  - `owned_surfaces` for files or areas the stream may edit
123
120
  - `shared_surfaces` for contracts, APIs, schemas, or docs touched with care
@@ -133,7 +130,7 @@ user asks to move to Chief/Streams. Do not teardown, merge, rebase, or remove
133
130
  worktrees as part of adoption. Adoption only creates stream metadata around
134
131
  existing M10 state.
135
132
 
136
- 1. Read or create `.forge/streams/active.yml`.
133
+ 1. Ensure the `.forge/streams/` dir exists (`active.yml` is derived — it will be regenerated by the rollup in step 8, not hand-created).
137
134
  2. Discover live M10 worktrees from both sources:
138
135
  - `git worktree list --porcelain` branches matching `refs/heads/forge/m-*`
139
136
  - `.forge/state/milestone-*.yml` with
@@ -163,6 +160,14 @@ existing M10 state.
163
160
  `{orchestration.worktree_root or ../<repo>-worktrees}/{anchor}` — rather
164
161
  than assuming the experimental `orchestrating` skill's internal default.
165
162
  Confirm the resolved path with `git worktree list` before recording it.
163
+ **If a path *is* recorded, validate it before honoring it** (don't trust it
164
+ blind — that is the gap a recorded-but-wrong path slips through): compare its
165
+ parent dir to the resolved convention root (`orchestration.worktree_root`,
166
+ else `{repo-basename}-worktrees`). Mismatch → advisory warn *"recorded
167
+ worktree path `{path}` is not under the convention root `{root}` — a manual or
168
+ pre-convention `git worktree add`; `git worktree move` it and update the
169
+ record."* Adopt the recorded path as-is regardless (advisory, never a block;
170
+ honors `orchestration.worktree_root`).
166
171
  - `runtime.worker_sessions` with the M10 session / claim ids
167
172
  - `ownership.owned_surfaces`, `shared_surfaces`, and `read_only_surfaces`
168
173
  as empty lists if unknown
@@ -174,7 +179,7 @@ existing M10 state.
174
179
  - known blockers
175
180
  - unknown ownership surfaces marked as needing declaration
176
181
  - next action from milestone state or "declare ownership and sync"
177
- 8. Add or update the compact entry in `.forge/streams/active.yml`.
182
+ 8. **Regenerate `active.yml` via the Stream Rollup** (do not hand-write the entry); set the adopted stream's `stream.milestone` link so its phase derives from milestone state.
178
183
  9. Run conflict detection. Missing ownership is a conflict until clarified, but
179
184
  it does not block adoption.
180
185
  10. Ask before changing `.forge/project.yml`:
@@ -190,14 +195,14 @@ or path, update its summary and brief instead of creating a duplicate stream.
190
195
  Pause:
191
196
  1. Update the stream brief with current state, decisions, blockers, and next
192
197
  action.
193
- 2. Set registry status to `paused`, unless work was handed to another session
194
- and should be `delegated`.
198
+ 2. Set the **stream file's** `status` to `paused` (or `delegated` if handed off),
199
+ then regenerate `active.yml` via the Stream Rollup.
195
200
  3. Clear `current_stream` only if no stream remains active in this session.
196
201
 
197
202
  Resume:
198
- 1. Load registry, stream file, and brief.
203
+ 1. Run the Stream Rollup, then load the stream file and brief.
199
204
  2. Surface status, blockers, owned surfaces, shared surfaces, and next action.
200
- 3. Set the stream to `active`.
205
+ 3. Set the **stream file's** `status` to `active`, then regenerate `active.yml`.
201
206
  4. Run conflict detection before continuing.
202
207
 
203
208
  ## Delegate Flow
@@ -262,7 +267,7 @@ reason it cannot merge yet.
262
267
  drifts from main, staleness accrues on shared surfaces, and the eventual merge is
263
268
  larger and riskier. This check makes the cadence visible so integration happens
264
269
  when work is ready — not at milestone end. Run it at the **start of the Show and
265
- Sync intents**, after the Registry Drift Check.
270
+ Sync intents**, after the Stream Rollup (which gives it a fresh derived registry).
266
271
 
267
272
  **Advisory — it surfaces and recommends. It never merges, never rebases, and
268
273
  never blocks.** Merging and rebasing stay human decisions (conflicts need
@@ -304,7 +309,8 @@ line (`merge cadence: nothing ready, no rebases needed`) and continue.
304
309
 
305
310
  1. Confirm final status: merged, intentionally abandoned, superseded, or split.
306
311
  2. Record final outcome in the stream brief.
307
- 3. Update registry status to `closed`.
312
+ 3. Set the **stream file's** `status` to `closed`, then regenerate `active.yml`
313
+ via the Stream Rollup.
308
314
  4. Clear `current_stream` if this was the active stream.
309
315
  5. Keep closed stream history available until a later archive/compaction pass.
310
316
 
@@ -45,7 +45,16 @@ git worktree list --porcelain 2>/dev/null \
45
45
 
46
46
  Cross-machine note: the flag file is machine-local, so a worktree on another laptop relies on the opt-in `forge.worktree_rebase_check` / a periodic fetch instead — eventual, not instant.
47
47
 
48
- **Ready-to-merge nudge (main checkout / streams without checkpoints).** If `.forge/streams/active.yml` exists, scan for streams with `status: ready` or a non-empty `merge_queue` **that are not auto-publishing via checkpoints**. Any present one line: *"{N} stream(s) ready to merge run `/chief-of-staff` (merge safe) to integrate."* Pointer only the cadence logic lives in the Chief's Merge Cadence Check; `forge` never merges. Nothing pending → silent.
48
+ **Worktree path convention check (advisory main or worktree).** A recorded worktree path is trusted *verbatim* everywhere (FORGE.md → "Recorded path is authoritative"), so a path created **outside** the convention a manual `git worktree add ../forge-worktrees/m115`, or pre-convention state is honored unchallenged by the 1.1a gate and by `chief-of-staff` adoption. This check surfaces that drift. It never blocks and never moves anything.
49
+
50
+ - Resolve the convention root: `orchestration.worktree_root` from `project.yml` (relative → against the **repo root**; absolute / leading-`~` honored verbatim); if unset, `{repo-parent}/{repo-basename}-worktrees`.
51
+ - For each recorded worktree path in play this boot — `lifecycle.worktree_path` of any surfaced live milestone, plus any stream `runtime.worktree` — compare its **parent dir** to the resolved root.
52
+ - Mismatch → one advisory line: *"recorded worktree path `{path}` for m{id} is not under the convention root `{root}` — likely a manual `git worktree add` or pre-convention state. Fix: `git worktree move {path} {root}/{anchor}` then update the recorded path. (Advisory; honors `orchestration.worktree_root`.)"*
53
+ - Parent dir under the root, or no recorded path → silent. An explicit `orchestration.worktree_root` is honored by construction (the override **is** the root compared against), so a deliberately relocated root never false-warns.
54
+
55
+ **Stream Rollup (active.yml is derived).** If `.forge/streams/` has any stream files, regenerate `.forge/streams/active.yml` from them + active milestones per FORGE.md → Stream Rollup, the same way step 1.0 regenerates `index.yml`. `active.yml` is derived — never read it as authoritative without rolling up first, and never hand-edit it. Runs in the main checkout (worktrees never write `active.yml`). No `streams/` dir → skip.
56
+
57
+ **Ready-to-merge nudge (main checkout / streams without checkpoints).** From the just-regenerated `active.yml`, scan for streams with `coordination: ready` or a non-empty `merge_queue` **that are not auto-publishing via checkpoints**. Any present → one line: *"{N} stream(s) ready to merge — run `/chief-of-staff` (merge safe) to integrate."* Pointer only — the cadence logic lives in the Chief's Merge Cadence Check; `forge` never merges. Nothing pending → silent.
49
58
 
50
59
  ### 1.0 State Rollup (index.yml is derived)
51
60
 
@@ -63,6 +72,8 @@ Rollup procedure (deterministic + idempotent):
63
72
 
64
73
  Output is a pure function of the milestone files, so two sessions regenerating it produce identical bytes — it never needs a hand-merge. **Only the main/orchestrator session runs rollup; worktree agents never write `index.yml`** (they edit only their own `milestone-{id}.yml`).
65
74
 
75
+ **Enforced, not optional — a boot must not read a stale registry.** The rollup *is* the reconcile step; skipping it silently lets `index.yml` drift from the milestone files — e.g. a worktree sets `current.status: complete` and merges to main, but `index.yml` stays `active` until some later boot happens to regenerate by hand (observed twice for m-CLOUDS01). So every boot **runs** steps 1–4. Before rewriting, diff each milestone's *derived* status against its current `index.yml` entry; any mismatch → emit one loud line — *"index.yml was stale vs {N} milestone file(s) ({ids}) — regenerated."* — then write. Identical (the common case) → the rewrite is a no-op by bytes; stay silent. The rollup is idempotent, so running it on every boot is free.
76
+
66
77
  ### 1.1 Milestone Selection
67
78
 
68
79
  Check state files:
@@ -111,6 +122,12 @@ Single-owner-mutable state must be written by exactly one checkout. When this se
111
122
 
112
123
  Resolve the worktree path from the recorded `lifecycle.worktree_path` / `runtime.worktree`, or — if absent — from the base **Worktree Convention** (FORGE.md).
113
124
 
125
+ **Completed milestone + live worktree → finalize, not gate (precedence).** The gate assumes the live worktree holds *in-progress, owned* work. A worktree whose milestone file is already `current.status: complete` — it set complete and merged to main, then a fresh main boot lands here — needs the **opposite** of a write-block: it needs wrapping up. So when the selected milestone is `complete` **and** still has a live worktree, do **not** raise the four-option ownership gate. Surface the finalize offer instead: *"m{id} is complete but its worktree `{path}` (`{branch}`) is still live — roll up `index.yml` and tear down the worktree + branch? [yes / keep]"*.
126
+ - **yes** → confirm, then `git worktree remove {path}`, delete the branch, clear `lifecycle.worktree_*` on the milestone, and run **Rollup (1.0)**. (Never auto-remove without the explicit yes.)
127
+ - **keep** → leave it; impose no write-block — a `complete` milestone has no live cursor left to fork.
128
+
129
+ The gate (below) and this finalize path are mutually exclusive on `current.status`: `complete` → finalize; any other status with a live worktree → gate.
130
+
114
131
  **When it does NOT trigger** (no-op — continue to routing): no live worktree for the selected milestone; or this session **is** the owning worktree (`in_main_checkout: false` and the branch anchors to this milestone); or the milestone has a recorded, still-current `lifecycle.ownership_override` (see option 4).
115
132
 
116
133
  **When it triggers — STOP. Do not route** to `planning`, `executing`, `verifying`, `reviewing`, `quick-tasking` against this milestone, and do not write any of:
@@ -63,6 +63,8 @@ Worktrees are a **base concern** — Chief/Streams creates and references them w
63
63
 
64
64
  **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.
65
65
 
66
+ **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.
67
+
66
68
  ## Workflow Tiers
67
69
 
68
70
  Auto-detects complexity. Override: "Use Quick/Standard/Full tier."
@@ -202,8 +204,8 @@ State lives in `.forge/`:
202
204
  - `state/desire-paths/` — Append-only framework-usage observations, one file per observation. Occurrence counts derived by globbing (no mutable counter). Each carries `scope: project | framework` (set at capture; absent ⇒ project). **`forge` boot is the single review owner**: at 3+ occurrences it surfaces a concrete fix from the co-located suggestion table, persists declines to `declined/` (no re-nag), and for `scope: framework` signals routes them upstream — a portable block to `upstream/` plus a `gh issue` offer against `forge.upstream_repo`. This closes Principle 7's capture→act loop; `verifying` captures but no longer surfaces. See [ADR-012](../docs/decisions/ADR-012-upstream-desire-path-feedback-channel.md).
203
205
  - `context.md` — Active locked decisions + deferred ideas (target <=12KB)
204
206
  - `context-archive.md` — Historical locked decisions; load only when relevant
205
- - `streams/active.yml` — Compact Project Chief traffic map: active stream summaries only.
206
- - `streams/{stream}.yml` — Per-stream state: lifecycle, ownership, shared surfaces, blockers, merge readiness.
207
+ - `streams/active.yml` — **DERIVED** Project Chief traffic map. Regenerated by the Stream Rollup from the per-stream files + active milestone files (see Stream Rollup below). **Never hand-edited**, like `index.yml`. Each row carries the two orthogonal dimensions side by side: `phase` (from the milestone, for milestone-backed streams) and `coordination` (from the stream file). See [ADR-015](../docs/decisions/ADR-015-derived-stream-registry.md).
208
+ - `streams/{stream}.yml` — Per-stream **source of truth**: coordination lifecycle, ownership, shared surfaces, blockers, merge readiness, and an optional `stream.milestone: m-{id}` link. Standalone (quick-fix) streams own their `status` here; milestone-backed streams omit workflow phase (the rollup reads it from the milestone).
207
209
  - `streams/{stream}/brief.md` — Human-readable stream context packet loaded when resuming that stream.
208
210
  - `streams/{stream}/packages/{id}.yml` — Work package contract for delegated workers or manual sessions.
209
211
  - `research/milestone-{id}.md` — Research findings snapshot (dated, immutable)
@@ -215,6 +217,15 @@ State lives in `.forge/`:
215
217
 
216
218
  **Milestones** group phases into concurrent streams. Own state file — no conflicts across sessions. Can be deferred (frozen in place) or archive-deleted.
217
219
  `index.yml status` gates routing: `not_started | active | deferred | complete`.
220
+
221
+ **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`, blockers, next action — all from the stream file.
223
+ 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
+ 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
+ 4. Derive `merge_queue:` — one entry per stream with `merge.readiness: ready`.
226
+ 5. Write `active.yml` (`version`, `updated_at`, derived `streams:` sorted by id, derived `merge_queue:`). No hand-edited keys.
227
+
228
+ 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.
218
229
  **Format**: YAML for machine state, Markdown for human content.
219
230
  **`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).
220
231
 
@@ -237,7 +248,7 @@ with the work it describes. Every artifact has a sharing class:
237
248
  | Class | Files | Write rule |
238
249
  |---|---|---|
239
250
  | **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. |
240
- | **Derived** | `state/index.yml` | Never hand-edited. Regenerated by Rollup 1.0 from `milestone-*.yml`. Only the main/orchestrator session runs rollup. |
251
+ | **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. |
241
252
  | **Append-only shared** | `releases.yml` (version reservations), `state/desire-paths/*` (one file per observation) | Many writers OK; structure prevents collision. `releases.yml` uses the Version Reservation Protocol (append + commit + push before depending on the slot); desire-paths use distinct filenames so two writers never touch the same file. |
242
253
  | **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. |
243
254
  | **Stable shared** | `project.yml`, `constitution.md`, `design-system.md`, `roadmap.yml`, `FORGE.md` | Rarely changes after init. Canonical on main. Worktrees lag and pull on rebase. No special protocol — git handles it because the conflict surface is tiny. |
@@ -0,0 +1,78 @@
1
+ # Migration Guide: Derived stream registry / Stream Rollup (Forge 0.37.0)
2
+
3
+ Applies to projects using Chief/Streams that have a **hand-maintained
4
+ `.forge/streams/active.yml` with stream entries**. `active.yml` becomes a
5
+ **derived projection** (ADR-015) — regenerated by the Stream Rollup from the
6
+ per-stream files + active milestones, never hand-edited. This is the durable fix
7
+ for issue [#4](https://github.com/Attuned-Media/forge/issues/4) and supersedes
8
+ the 0.33.0 Registry Drift Check.
9
+
10
+ The only thing that needs migrating is **data that lives only in `active.yml`**:
11
+ if a stream was tracked as a registry row but has no `.forge/streams/{stream}.yml`
12
+ source file, the rollup (which reads per-stream files) would drop it. The
13
+ migration materializes those files first, then lets the rollup regenerate
14
+ `active.yml`.
15
+
16
+ Projects with no streams, or whose `active.yml` is the empty stub, need nothing —
17
+ the next `forge` boot / `chief-of-staff` Show simply regenerates it in the derived
18
+ shape. (That is why Detection only fires when there are real stream rows.)
19
+
20
+ ## Prerequisites
21
+
22
+ 1. On the new version's framework files (`npx forge-orkes upgrade`, or the
23
+ `upgrading` skill).
24
+ 2. Working tree clean or changes committed — the migration writes per-stream
25
+ files and rewrites `active.yml`.
26
+
27
+ ## Detection
28
+
29
+ Fires only when `active.yml` is the **old hand-maintained shape** (no derived
30
+ marker) **and** carries real stream rows. Silent otherwise — already derived, no
31
+ streams, or no file:
32
+
33
+ ```bash
34
+ F=.forge/streams/active.yml
35
+ [ -f "$F" ] || exit 0
36
+ grep -q 'DO NOT HAND-EDIT' "$F" && exit 0 # already derived (new template marker)
37
+ if grep -qE '^[[:space:]]*-?[[:space:]]*id:[[:space:]]*\S' "$F"; then
38
+ echo "MIGRATE — active.yml is hand-maintained with stream entries (pre-ADR-015); materialize per-stream files, then it becomes derived"
39
+ fi
40
+ ```
41
+
42
+ ## Migration steps
43
+
44
+ ### 1. Ensure every registry stream has a source file
45
+
46
+ For each stream row in the current `active.yml`, confirm a
47
+ `.forge/streams/{id}.yml` exists. For any that don't, create one from
48
+ `.forge/templates/streams/stream.yml` and copy the row's data into it:
49
+
50
+ - `stream.status` ← the row's status (coordination lifecycle);
51
+ - `stream.milestone` ← `m-{id}` if the stream backs a milestone, else leave
52
+ `null` (standalone / quick-fix stream);
53
+ - `runtime.branch` / `runtime.worktree`, `ownership.*`, `merge.readiness`,
54
+ `blockers`, `next_action` ← from the row.
55
+
56
+ Do **not** copy a workflow phase into the stream file — that derives from the
57
+ linked milestone.
58
+
59
+ ### 2. Regenerate `active.yml` (derived)
60
+
61
+ Run `chief-of-staff` Show (or `/forge` boot) — it runs the Stream Rollup and
62
+ rewrites `active.yml` in the derived shape (header marker, `phase` + `coordination`
63
+ per row, derived `merge_queue`, plus `implicit` rows for active milestones with no
64
+ stream file). From here `active.yml` is regenerated, never hand-edited.
65
+
66
+ <!-- Lossy step (materializing files from registry rows) — confirm with the user
67
+ before writing if any row's data is ambiguous. Runs via quick-tasking. -->
68
+
69
+ ## Validation
70
+
71
+ - `head -5 .forge/streams/active.yml` shows the `DERIVED — DO NOT HAND-EDIT`
72
+ marker.
73
+ - Every stream that was in the old registry has a `.forge/streams/{id}.yml`
74
+ source file.
75
+ - Re-running `chief-of-staff` Show produces byte-identical `active.yml` (the
76
+ rollup is idempotent).
77
+ - No active milestone is missing from the map (active milestones with no stream
78
+ file appear as `stream: implicit` rows).
@@ -112,6 +112,11 @@ releases:
112
112
  bump: minor
113
113
  reserved_at: "2026-06-21"
114
114
  summary: "Stream Integration Checkpoints — event-driven continuous integration, the producer/flag/consumer follow-up to #8's polling-based cadence check. (1) planning marks integration_checkpoint phases in plan frontmatter (advisory; default = last phase + depended-on phases; the mark is the opt-in so unmarked/existing work is unaffected). (2) Producer: verifying, on a PASSED + human-verified checkpoint phase in a worktree, fast-forward-only publishes to main (git push origin HEAD:main — git rejects non-ff, which is the safety; diverged main is surfaced, never forced) and sets an integration flag in .git/forge/integration-pending (git common dir, shared across a machine's worktrees). (3) Consumer: forge boot in a worktree reads the flag cheaply — set + no divergence → auto git merge --ff-only main (conflict-free); diverged → surface a rebase prompt; not set → skip (no fetch, no scan). Supersedes the 0.35.0 forge-boot polling pointer with event-driven detection. Cross-machine falls back to opt-in worktree_rebase_check. Never force-pushes, auto-resolves a conflict, or auto-rebases."
115
+ - milestone: 16
116
+ version: "0.37.0"
117
+ bump: minor
118
+ reserved_at: "2026-06-22"
119
+ summary: "Derived stream registry / Stream Rollup (ADR-015) — the durable structural fix for issue #4's registry drift, superseding the 0.33.0 advisory Registry Drift Check. streams/active.yml becomes a DERIVED projection (like state/index.yml), never hand-edited, regenerated by a deterministic join-rollup over two single-sourced inputs: per-stream files streams/{stream}.yml (coordination lifecycle + ownership/runtime/merge/blockers) and active milestone files (workflow phase). Resolves the not-1:1 milestone↔stream relationship by keeping workflow phase and coordination status as orthogonal dimensions side by side in each derived row (phase from milestone, coordination from stream file) rather than collapsing one into the other; coverage is computed (active milestone with no stream file → derived 'implicit' row) so #4's exact symptom is structurally impossible. Adds stream.milestone link field (lazy; absent ⇒ standalone). forge boot + chief-of-staff Show/Sync run the Stream Rollup; chief Start/Pause/Resume/Close/Adopt write per-stream files then regenerate. Registry Drift Check removed. active.yml put in the derived state-ownership class (ADR-011); worktrees never write it. Migration: materialize per-stream files from a hand-maintained active.yml, then regenerate."
115
120
 
116
121
  # Note on the 15 / 0.24.0 ordering: reserved as max(0.23.0)+minor. origin/main was
117
122
  # already at 0.23.0 when this shipped (M10/M12/M13/M14 all merged), so 0.24.0 is a
@@ -1,32 +1,45 @@
1
1
  # Forge Streams — Active Project Map
2
- # Size gate: keep this file <= 20 KB. It is the Project Chief's default context.
3
- # Store summaries only. Deep stream history belongs in streams/{stream}/.
2
+ #
3
+ # DERIVED DO NOT HAND-EDIT. This file is a projection regenerated by the
4
+ # Stream Rollup (FORGE.md → Stream Rollup) from two sources:
5
+ # - per-stream files .forge/streams/{stream}.yml (coordination + stream-only state)
6
+ # - active milestones .forge/state/milestone-*.yml (workflow phase)
7
+ # Edit those sources, then let `forge` boot or `chief-of-staff` Show/Sync
8
+ # regenerate this file. Hand edits are overwritten on the next rollup.
9
+ #
10
+ # Size gate: keep this file <= 20 KB. Summaries only; deep history lives in
11
+ # streams/{stream}/. See ADR-015.
4
12
 
5
13
  version: 1
6
- updated_at: null
14
+ updated_at: null # set by the rollup
7
15
 
8
16
  project_chief:
9
17
  current_stream: null
10
18
  merge_policy: "chief-owned"
11
19
  notes: ""
12
20
 
21
+ # Each row is DERIVED. `phase` comes from the linked milestone (milestone-backed
22
+ # rows); `coordination` comes from the stream file. `stream: implicit` marks an
23
+ # active milestone that has no stream file yet (coverage — materialize a stream
24
+ # file if it needs coordination).
13
25
  streams:
14
26
  # - id: "billing-retries"
15
- # status: active # draft | active | paused | delegated | blocked | ready | merged | closed
27
+ # stream: explicit # explicit (has a stream file) | implicit (milestone-only)
28
+ # milestone: "m-12" # link, if milestone-backed; absent ⇒ standalone
29
+ # phase: executing # DERIVED from milestone current.status (backed rows only)
30
+ # coordination: active # DERIVED from stream file status: draft|active|paused|delegated|blocked|ready|merged|closed
16
31
  # goal: "Fix checkout retry handling"
17
32
  # branch: "forge/stream-billing-retries"
18
33
  # worktree: "../app-worktrees/billing-retries"
19
- # owner: "" # session/thread/user label
20
- # owned_surfaces:
21
- # - "src/billing/**"
22
- # shared_surfaces:
23
- # - "src/lib/events.ts"
34
+ # owned_surfaces: ["src/billing/**"]
35
+ # shared_surfaces: ["src/lib/events.ts"]
36
+ # merge_readiness: not_ready # not_ready | ready | blocked | merged
24
37
  # risk: medium # low | medium | high | unknown
25
38
  # blocked_by: []
26
39
  # next_action: "Review event contract before editing shared surface"
27
- # last_sync: null
28
40
  []
29
41
 
42
+ # DERIVED — one entry per stream whose stream file has merge.readiness: ready.
30
43
  merge_queue:
31
44
  # - stream: "billing-retries"
32
45
  # status: waiting # waiting | merging | blocked | merged
@@ -7,7 +7,10 @@ version: 1
7
7
  stream:
8
8
  id: ""
9
9
  goal: ""
10
- status: draft # draft | active | paused | delegated | blocked | ready | merged | closed
10
+ status: draft # COORDINATION lifecycle: draft | active | paused | delegated | blocked | ready | merged | closed
11
+ milestone: null # link to a milestone (e.g. "m-12"); null ⇒ standalone (quick-fix) stream.
12
+ # Set → Stream Rollup derives workflow `phase` from the milestone; do NOT
13
+ # duplicate workflow status here. This `status` stays the coordination dimension.
11
14
  tier: null # quick | standard | full | null until known
12
15
  created_at: null
13
16
  updated_at: null