forge-orkes 0.28.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forge-orkes",
3
- "version": "0.28.0",
3
+ "version": "0.29.0",
4
4
  "description": "Set up the Forge meta-prompting framework for Claude Code in your project",
5
5
  "bin": {
6
6
  "create-forge": "./bin/create-forge.js"
@@ -42,13 +42,23 @@ Never accumulate decisions in working memory. Never batch writes to convergence.
42
42
 
43
43
  ### Write Protocol
44
44
 
45
+ `context.md` is **shared mutable** state per FORGE.md State Ownership — writes follow two structural rules:
46
+ - **Scope to your milestone's block.** Different milestone blocks → git merges cleanly across worktrees.
47
+ - **Append-only within a block.** When superseding an older decision, *strike through* (`~~old decision~~`) and append the new line below — never rewrite in place. Same-line concurrent edits become impossible by construction; reading the history of a decision becomes possible by inspection.
48
+
45
49
  **On first decision of the session:**
46
50
  - Check if `.forge/context.md` exists. If not → create it from `.forge/templates/context.md`
47
51
  - Add a milestone heading inside `## Locked Decisions`: `### M{id} — {name} (drafting)` (use "M?" if no milestone yet)
52
+ - **Cross-tree liveness check (informational).** If editing an existing block for milestone `{id}`, run `git worktree list --porcelain | awk '/^branch / && $2 ~ /refs\/heads\/forge\/m-'"$id"'(-|$)/ {print}'`. Any hit means m{id} is **live in another worktree** — surface one line: *"m{id} is live in worktree at `{path}`. Edits land cleanly on main but won't be visible to that worktree until it rebases or pulls."* Proceed with the write — main is often the canonical author of `context.md`, this is a heads-up, not a block.
48
53
 
49
54
  **On each confirmed decision:**
50
55
  - Append under the milestone heading: `- **[Topic]**: [Decision]. Reason: [Why]`
51
56
 
57
+ **On superseding a prior decision (in your own milestone's block):**
58
+ - Find the original line. Wrap it in `~~ ~~` (markdown strikethrough). DO NOT delete it.
59
+ - Append the new decision on a new line below it: `- **[Topic]**: [Decision]. Reason: [Why]. Supersedes ~~prior on {date}~~.`
60
+ - Why: shared-mutable append-only rule (FORGE.md State Ownership). Decisions stay readable as history; concurrent writes from another worktree on a different bullet can't conflict with this one.
61
+
52
62
  **On deferred ideas** (user says "not now" / "later" / "skip"):
53
63
  - Append to `## Deferred Ideas` immediately: `- [Idea] — Deferred because: [reason]`
54
64
 
@@ -9,6 +9,33 @@ Entry point. Detect tier, route skills, manage transitions. New projects → ini
9
9
 
10
10
  ## Step 1: Read State
11
11
 
12
+ **Preflight (multi-tree visibility) — run before everything in this section.**
13
+
14
+ The presence of live Forge worktrees changes the meaning of "which milestone am I editing" — the operator needs to see the picture before any state edit. Cheap to compute, free if there's nothing to show.
15
+
16
+ ```bash
17
+ # This session's checkout — main or a worktree? --git-dir == --git-common-dir → main; differ → linked worktree.
18
+ this_dir=$(git rev-parse --git-dir 2>/dev/null) || exit 0
19
+ common_dir=$(git rev-parse --git-common-dir)
20
+ in_main_checkout=$([ "$this_dir" = "$common_dir" ] && echo true || echo false)
21
+
22
+ # Forge worktrees = branch matches forge/m-*
23
+ git worktree list --porcelain 2>/dev/null \
24
+ | awk '
25
+ /^worktree / { path=$2 }
26
+ /^branch / { br=$2; if (br ~ /refs\/heads\/forge\/m-/) print path " " br }
27
+ '
28
+ ```
29
+
30
+ **If `in_main_checkout: true`** AND any Forge worktrees are reported:
31
+ - Surface them at the top of the boot output, one line each: *"m{id} is live in worktree `{path}` on branch `{branch}`. Edits to its milestone state belong there; shared-doc edits here (`context.md`, `refactor-backlog.yml`) won't be visible to it until it rebases or pulls."*
32
+ - This is informational — `forge` continues to Rollup + selection. The operator now knows. (See FORGE.md State Ownership for the full taxonomy.)
33
+
34
+ **If `in_main_checkout: false`** (this session is itself a worktree) AND `forge.worktree_rebase_check: true` in `.forge/project.yml` (default `false` — opt-in):
35
+ - Diff `.forge/` between this worktree's `HEAD` and `origin/main` (or `main` if no remote), restricted to the **shared-mutable** + **stable-shared** files per the FORGE.md taxonomy (`context.md`, `refactor-backlog.yml`, `project.yml`, `constitution.md`, `design-system.md`, `roadmap.yml`, `requirements/m{N}.yml`).
36
+ - Any differ → surface: *"`{file}` has changed on main since this worktree branched. Pull just this file (`git restore --source main -- {file}`), rebase, or skip?"* — operator decides.
37
+ - Setting absent or `false` → no-op.
38
+
12
39
  ### 1.0 State Rollup (index.yml is derived)
13
40
 
14
41
  `index.yml` is a **derived registry** — regenerate it from the milestone files before reading, and after any milestone CRUD (promote/defer/resume/delete). **Never hand-edit `index.yml`.**
@@ -312,6 +312,12 @@ Deferred issues triage: If `.forge/deferred-issues.md` has `status: pending` ite
312
312
 
313
313
  ### Backlog
314
314
 
315
+ `refactor-backlog.yml` is **shared mutable** state per FORGE.md State Ownership — many milestones append over time. Two rules apply:
316
+ - **Items carry `milestone:` scoping** (already enforced by the schema below) — concurrent appends from different milestones touch different items, so git merges cleanly.
317
+ - **Existing items are append-only** in spirit: change `status:` and add `completed:`/`deferred_at:` rather than rewriting the item's body. Compaction-on-write (below) handles physical relocation of terminal items.
318
+
319
+ **Cross-tree liveness check (informational).** Before writing, run `git worktree list --porcelain | awk '/^branch / && $2 ~ /refs\/heads\/forge\/m-/ {print}'`. Any non-empty output means another milestone is live in a worktree — surface one line: *"{N} other Forge milestone(s) live in worktrees ({list}). Backlog appends here land cleanly on main but won't be visible to those worktrees until they rebase."* Proceed with the write — reviewing on main is the canonical author. (Two reviewing sessions writing in parallel from different worktrees rely on the milestone-scoped append rule above, not on this check.)
320
+
315
321
  Read `.forge/refactor-backlog.yml`. Next ID = max + 1. Append:
316
322
 
317
323
  ```yaml
@@ -162,9 +162,38 @@ State-sync commits are separate from per-task code commits (which stay atomic du
162
162
 
163
163
  ### State Ownership (multi-worktree safety)
164
164
 
165
- - `state/milestone-{id}.yml` is the **single source of truth**; exactly one agent owns it at a time.
165
+ `.forge/` is **code-like**: it branches with the repo, syncs via git, atomic-commits with the work it describes. Worktrees get their own `.forge/` checkout by construction — that's not configuration, that's git. The framework's job is to specify how each *kind* of `.forge/` file behaves under that model.
166
+
167
+ **Every `.forge/` artifact falls into one of five sharing classes.** The class determines the write rule across worktrees:
168
+
169
+ | Class | Files | Write rule |
170
+ |---|---|---|
171
+ | **Single-owner mutable** | `state/milestone-{id}.yml`, `phases/milestone-{id}/`, `research/milestone-{id}.md` | Exactly one writer at a time — the worktree (or main session) currently driving that milestone. **Other worktrees never touch it**, not even to read-then-write — they pull from main if they need it. |
172
+ | **Derived** | `state/index.yml` | Never hand-edited. Regenerated by Rollup 1.0 from `milestone-*.yml`. Only the main/orchestrator session runs rollup. |
173
+ | **Append-only shared** | `releases.yml` (version reservations), `state/desire-paths/*` (one file per observation) | Many writers OK; structure prevents collision. `releases.yml` uses the Version Reservation Protocol (append + commit + push before depending on the slot); desire-paths use distinct filenames so two writers never touch the same file. |
174
+ | **Shared mutable** | `context.md`, `refactor-backlog.yml`, `requirements/m{N}.yml` (ID space `FR-`/`DEF-`/`NFR-` is globally shared even though each milestone owns its file) | Two rules: **(a) Write only to YOUR milestone's block.** Different milestone blocks → git merges cleanly. **(b) Within a block, append-only.** Locked decisions get *struck through* (`~~old~~`) rather than rewritten in place; new sub-bullets appended below existing ones. Same-line concurrent edits become impossible by construction. |
175
+ | **Stable shared** | `project.yml`, `constitution.md`, `design-system.md`, `roadmap.yml`, `FORGE.md` | Rarely changes after init. Canonical on main. Worktrees lag and pull on rebase. No special protocol — git handles it because the conflict surface is tiny. |
176
+
177
+ **Cross-tree write awareness.** When a session edits a shared-mutable file's block whose milestone is **live in a different worktree** (e.g. main writes to `context.md`'s `## M9` block while m9 is running in a worktree), the edit lands cleanly on main but is **invisible to the worktree until it pulls/rebases**. This is identical to how `src/foo.ts` works under branches — there's nothing pathological about it; it's branch divergence. The framework surfaces the situation so the operator can decide:
178
+
179
+ - **`forge` Step 1** in main checks `git worktree list` and surfaces live Forge worktrees at boot — *"m9 is live in worktree X at e94b6bb. Edits to m9 state belong there; shared docs edited here need a sync."*
180
+ - **`discussing`** (before writing to `context.md`'s milestone block) and **`reviewing`** (before writing to `refactor-backlog.yml`) emit a one-line warning when the target block's milestone is live elsewhere. The warning informs, never blocks — main is often the canonical author of these files.
181
+ - **Worktree-side rebase check** (opt-in via `forge.worktree_rebase_check: true` in `project.yml`) — when `forge` boots inside a worktree, it diffs `.forge/` between the worktree's HEAD and `main`; if shared-mutable files have changed on main, it offers `git restore --source main -- <files>` or a rebase.
182
+
183
+ **Why not global `.forge/`?** A common instinct is "make `.forge/` shared so context propagates automatically." It breaks four load-bearing invariants:
184
+ 1. **Decisions are contracts** — they're versioned with the code they govern; severing them from branches breaks the contract.
185
+ 2. **State survives machine loss** — the multi-laptop pull-from-any-clone story depends on `.forge/` being in git.
186
+ 3. **History time-travel** — `git checkout` an old commit and you see the plan/decisions as they were; global state makes that impossible.
187
+ 4. **Git is the concurrency detector** — without it, two sessions writing the same field becomes silent last-write-wins.
188
+
189
+ Per-worktree `.forge/` with a sharing taxonomy is the model. See [ADR-011](../docs/decisions/ADR-011-shared-state-taxonomy.md) for the longer version.
190
+
191
+ ### State Ownership rules (quick reference)
192
+
193
+ - `state/milestone-{id}.yml` is the **single source of truth**; exactly one agent owns it at a time. **Other worktrees never write it** — promoted from advice to explicit rule, because catching yourself before the first wrong edit is the part the framework can help with.
166
194
  - Worktree / parallel agents write **only** their own milestone file and **append** desire-path files. They **never** write `index.yml`.
167
195
  - `index.yml` is **regenerated by rollup** (read every `milestone-*.yml` → rewrite the registry) by the main/orchestrator session — on `forge` resume and at `orchestrating` teardown. The rollup is deterministic + idempotent, so it **is** the reconcile step — never a hand-merge.
196
+ - For shared-mutable files: write only your milestone's block; within a block, append-only (strike through, don't rewrite).
168
197
  - Same-milestone parallel work is out of scope, guarded by the M10 claim layer.
169
198
 
170
199
  ### Version Reservation Protocol
@@ -0,0 +1,68 @@
1
+ # Migration Guide: Shared-state taxonomy (Forge 0.29.0)
2
+
3
+ Applies to any Forge project that uses git worktrees (M10 or hand-rolled). This is a **documentation + skill-behavior** change, not a state-file change — there is nothing in `.forge/` to migrate. Read this once, then the new skill behavior catches the situations.
4
+
5
+ ## What changed and why
6
+
7
+ Forge's `State Ownership (multi-worktree safety)` section in `FORGE.md` previously documented two file classes — `state/milestone-{id}.yml` (single-owner) and `state/index.yml` (derived) — and was silent on every other file in `.forge/`. In practice three more classes exist, and the riskiest one (shared mutable — `context.md`, `refactor-backlog.yml`) had no rule.
8
+
9
+ A worked example from canvaz on 2026-06-15: a main-session edit to `context.md`'s `## M9` block was invisible to the live m9 worktree, because the worktree was branched off main earlier and reads `.forge/` from its own checkout. The operator had to deduce the sync gap from first principles, and nearly committed `milestone-9.yml` edits from main on the first attempt before catching themselves.
10
+
11
+ 0.29.0 closes the gap with **taxonomy + visibility + structural rules**, not with a state migration. See [ADR-011](../decisions/ADR-011-shared-state-taxonomy.md) for the longer version (including why a "global `.forge/`" approach is wrong).
12
+
13
+ ## The 5-class taxonomy (read this once)
14
+
15
+ | Class | Examples | Write rule |
16
+ |---|---|---|
17
+ | **Single-owner mutable** | `state/milestone-{id}.yml`, `phases/milestone-{id}/`, `research/milestone-{id}.md` | One writer at a time; other worktrees **never** touch it. Explicit rule. |
18
+ | **Derived** | `state/index.yml` | Never hand-edited; regenerated by Rollup 1.0. Only main/orchestrator runs rollup. |
19
+ | **Append-only shared** | `releases.yml`, `state/desire-paths/*` | Many writers OK; structure prevents collision. Append + commit + push before depending on the slot. |
20
+ | **Shared mutable** | `context.md`, `refactor-backlog.yml`, `requirements/m{N}.yml` | (a) Write only to YOUR milestone's block. (b) Within a block, append-only — strike through (`~~old~~`) superseded decisions instead of rewriting in place. |
21
+ | **Stable shared** | `project.yml`, `constitution.md`, `design-system.md`, `roadmap.yml`, `FORGE.md` | Rarely changes after init; canonical on main; worktrees lag and pull on rebase. No protocol — git handles it. |
22
+
23
+ The full canonical version lives in `FORGE.md` → `State Ownership`.
24
+
25
+ ## What the framework now does automatically
26
+
27
+ After upgrading to 0.29.0:
28
+
29
+ - **`forge` Step 1 preflight (main checkout):** before Rollup, surfaces any live Forge worktrees and their owning milestones — *"m9 is live in worktree X at e94b6bb. Edits to its milestone state belong there; shared docs edited here need a sync."* No prescription, just visibility.
30
+ - **`discussing`** emits a one-line warning before writing to `context.md`'s milestone block if that milestone is live in another worktree.
31
+ - **`reviewing`** emits the same warning before appending to `refactor-backlog.yml` if any milestone is live in a worktree.
32
+ - **`discussing`** also documents the supersede protocol (strike-through old decisions, append new ones below) so concurrent edits across worktrees stay mergeable.
33
+
34
+ ## Optional: enable the worktree-side rebase check
35
+
36
+ Opt-in; default off. When enabled, `forge` boot **inside a worktree** diffs `.forge/` against `main` and offers to pull individual shared-mutable / stable-shared files when they've moved.
37
+
38
+ Add to `.forge/project.yml`:
39
+
40
+ ```yaml
41
+ forge:
42
+ worktree_rebase_check: true
43
+ ```
44
+
45
+ Trade-off: adds one git diff per `forge` boot inside worktrees. Worth it for projects that frequently update `context.md` / `constitution.md` on main while worktrees are mid-flight; overkill for projects where the main checkout is mostly read-only.
46
+
47
+ ## Existing decisions in context.md — do nothing
48
+
49
+ The taxonomy is descriptive: every existing decision in `context.md` already fits the new rules (it lives under a milestone heading, was append-only in practice). No rewrite needed. **New** writes follow the protocol (use strike-through to supersede).
50
+
51
+ If a past edit broke the rule (decision rewritten in place), don't go back and reconstruct strike-throughs — the history is in git. Just follow the rule going forward.
52
+
53
+ ## Detection (none needed)
54
+
55
+ There's nothing to detect on disk. The framework changes are in skills and `FORGE.md`. Upgrade and you have them.
56
+
57
+ ## Validation
58
+
59
+ After `npx forge-orkes upgrade`:
60
+
61
+ - `grep "5-class\|sharing classes" .forge/FORGE.md` returns hits in the `State Ownership` section.
62
+ - `grep "cross-tree liveness\|append-only" .claude/skills/discussing/SKILL.md` returns the Write Protocol additions.
63
+ - `grep "worktree-liveness\|worktree_rebase_check" .claude/skills/forge/SKILL.md` returns the Step 1 preflight.
64
+
65
+ Then, if you have a worktree active:
66
+
67
+ - `forge` in the main checkout surfaces the live worktree at the top of its boot output.
68
+ - Begin a discussion in main that touches a worktree-live milestone → `discussing` emits the cross-tree warning before its first write.