forge-orkes 0.77.0 → 0.80.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.
Files changed (41) hide show
  1. package/bin/create-forge.js +27 -0
  2. package/experimental/m10/README.md +4 -4
  3. package/experimental/m10/install.sh +10 -9
  4. package/experimental/m10/source/hooks/protect-primary-checkout.sh +149 -0
  5. package/package.json +1 -1
  6. package/template/.claude/hooks/forge-context-migrate.sh +297 -0
  7. package/template/.claude/hooks/forge-size-gate.sh +175 -0
  8. package/template/.claude/hooks/forge-state-rollup.sh +135 -2
  9. package/template/.claude/hooks/protect-primary-checkout.sh +149 -0
  10. package/template/.claude/hooks/skill-gate-tiers.txt +6 -0
  11. package/template/.claude/hooks/tests/forge-context-migrate.test.sh +166 -0
  12. package/template/.claude/hooks/tests/forge-size-gate.test.sh +132 -0
  13. package/template/.claude/hooks/tests/protect-primary-checkout.test.sh +147 -0
  14. package/template/.claude/settings.json +14 -1
  15. package/template/.claude/skills/discussing/SKILL.md +30 -31
  16. package/template/.claude/skills/executing/SKILL.md +3 -3
  17. package/template/.claude/skills/forge/SKILL.md +112 -170
  18. package/template/.claude/skills/forge/references/manual-fallback-procedures.md +54 -0
  19. package/template/.claude/skills/initializing/SKILL.md +20 -1
  20. package/template/.claude/skills/planning/SKILL.md +11 -9
  21. package/template/.claude/skills/researching/SKILL.md +2 -0
  22. package/template/.claude/skills/reviewing/SKILL.md +17 -0
  23. package/template/.claude/skills/upgrading/SKILL.md +22 -2
  24. package/template/.claude/skills/verifying/SKILL.md +2 -2
  25. package/template/.forge/FORGE.md +81 -90
  26. package/template/.forge/bin/new-worktree.sh +61 -0
  27. package/template/.forge/bin/tests/new-worktree.test.sh +70 -0
  28. package/template/.forge/checks/forge-jarvis-awareness.sh +46 -2
  29. package/template/.forge/checks/forge-jarvis-relay.sh +64 -16
  30. package/template/.forge/checks/tests/forge-jarvis-awareness.test.sh +58 -0
  31. package/template/.forge/checks/tests/forge-jarvis-relay.test.sh +50 -0
  32. package/template/.forge/git-hooks/pre-commit +57 -0
  33. package/template/.forge/git-hooks/tests/pre-commit.test.sh +97 -0
  34. package/template/.forge/migrations/0.78.0-weld-primary-checkout.md +66 -0
  35. package/template/.forge/migrations/0.80.0-context-md-sharding.md +158 -0
  36. package/template/.forge/migrations/0.80.0-size-gate-wiring.md +145 -0
  37. package/template/.forge/templates/constitution.md +1 -1
  38. package/template/.forge/templates/context-milestone.md +30 -0
  39. package/template/.forge/templates/slice-runner/config.yml +7 -1
  40. package/template/.forge/templates/state/desire-path.yml +3 -0
  41. package/experimental/m10/source/hooks/forge-branch-guard.sh +0 -61
@@ -390,7 +390,26 @@ User selects per stack.
390
390
 
391
391
  **Confirm with the user first** in brownfield (this edits a user-owned file). In greenfield with no existing `.gitignore`, write it directly. Re-run is idempotent — the carve-out lines `grep` to themselves.
392
392
 
393
- 8. **State-sync commit**: `git add .gitignore .claude/settings.json .claude/skills/ .claude/agents/ .claude/hooks/ .forge/` then `git commit -m "chore(forge): initialize forge state + track product files"` (scoped; never `git add .`) so the new project's state and Forge product files are both in git from the start.
393
+ 8. **Wire the primary-checkout weld (`core.hooksPath`).** Welds the primary checkout to the default branch so all branch work happens in worktrees every tier (issue #21, ADR-031). The tracked `.forge/git-hooks/pre-commit` backstop only fires once `core.hooksPath` points at it. This is per-clone git config, so init owns it. **Detect + warn, never clobber:**
394
+
395
+ ```bash
396
+ want=".forge/git-hooks"
397
+ cur=$(git config --get core.hooksPath 2>/dev/null || true)
398
+ if [ -z "$cur" ] || [ "$cur" = "$want" ]; then
399
+ git config core.hooksPath "$want" # repo-relative → resolves inside every worktree
400
+ echo "[Forge] core.hooksPath → $want (primary-checkout weld active)."
401
+ else
402
+ echo "[Forge] core.hooksPath is already set to '$cur' (Husky/lefthook?). NOT overwriting."
403
+ echo " The primary-checkout pre-commit backstop (.forge/git-hooks/pre-commit) is"
404
+ echo " therefore inactive. To keep the weld, chain it into your existing hooks dir"
405
+ echo " (call .forge/git-hooks/pre-commit from your pre-commit), or accept the"
406
+ echo " PreToolUse-only coverage (Claude sessions are still guarded)."
407
+ fi
408
+ ```
409
+
410
+ **WHY repo-relative** (`.forge/git-hooks`, not an absolute path): an absolute `core.hooksPath` silently skips in linked worktrees (the documented Husky/lefthook worktree hazard — see `orchestrating/bootstrap-checks.md`); a repo-relative path resolves under each worktree checkout. **WHY never clobber:** silently disabling a consumer's existing hook framework is unacceptable.
411
+
412
+ 9. **State-sync commit**: `git add .gitignore .claude/settings.json .claude/skills/ .claude/agents/ .claude/hooks/ .forge/` then `git commit -m "chore(forge): initialize forge state + track product files"` (scoped; never `git add .`) — so the new project's state and Forge product files are both in git from the start. (The tracked `.forge/git-hooks/` is included by the `.forge/` path; `core.hooksPath` itself is local git config, not a tracked file, so it needs no staging.)
394
413
 
395
414
  *"Initialized. Ready?"*
396
415
 
@@ -31,20 +31,22 @@ If you find yourself writing a plan that only touches `src/models/`, `src/db/`,
31
31
 
32
32
  ## Step 1: Resolution Gate
33
33
 
34
- Read `.forge/context.md` **Needs Resolution**. If unchecked `- [ ]` items:
34
+ Read `.forge/context-log.md` **Needs Resolution** (Needs Resolution is not milestone-scoped — it lives in the log, not a per-milestone file). If unchecked `- [ ]` items:
35
35
  *"{N} items need input: [list]. For each: lock, defer, fix, or drop?"*
36
36
 
37
- Load `.forge/context.md` as the active decision set. Consult
37
+ Load `.forge/context/{id}.md` as the active decision set for milestone `{id}`. Consult
38
38
  `.forge/context-archive.md` only when the current milestone, stream, or
39
39
  requirement cites an older milestone decision (for example M9 e2e policy or M14
40
- backlog lifecycle). Do not load archived decision blocks wholesale.
40
+ backlog lifecycle) — check `.forge/context.md` (the derived index) first; if the
41
+ cited milestone has a row there, read its `context/{id}.md` directly instead of
42
+ the archive. Do not load archived decision blocks wholesale.
41
43
 
42
44
  | Choice | Action |
43
45
  |--------|--------|
44
46
  | Lock | Record in Locked Decisions |
45
47
  | Defer | Move to Deferred Ideas + revisit date |
46
48
  | Fix | Add as FR-xxx in `.forge/requirements/m{N}.yml` (current milestone) |
47
- | Drop | Note in Amendment Log |
49
+ | Drop | Note in Amendment Log (`.forge/context-log.md`) |
48
50
 
49
51
  Mark resolved `- [x]`. Target: 2-5 min.
50
52
  Scan **Carried Forward** -- items affecting current phase get same triage.
@@ -60,17 +62,17 @@ Record results in plan frontmatter.
60
62
 
61
63
  ## Step 3: Lock Decisions
62
64
 
63
- If missing, create `.forge/context.md` from template:
65
+ If missing, create `.forge/context/{id}.md` from `.forge/templates/context-milestone.md`:
64
66
  1. Ask user key decisions (framework, constraints, preferences)
65
67
  2. **Locked Decisions** (contracts)
66
68
  3. **Deferred Ideas** (out of scope)
67
69
  4. **Discretion Areas** (agent picks)
68
70
 
69
- Must complete BEFORE plans. Plans reference context.md.
71
+ Must complete BEFORE plans. Plans reference context/{id}.md.
70
72
 
71
73
  If a plan depends on an archived decision, cite the archive section in the plan
72
74
  or active context summary; do not copy the historical block back into
73
- `.forge/context.md`.
75
+ `.forge/context/{id}.md`.
74
76
 
75
77
  ## Step 4: Structure Requirements
76
78
 
@@ -396,7 +398,7 @@ Prompt pattern:
396
398
  > *"UI work detected. Invoke `testing` skill for e2e/integration coverage? (yes/no/later)"*
397
399
 
398
400
  - **yes** — defer to `testing` after planning completes; note in plan frontmatter
399
- - **no** — document rationale in `context.md` Discretion Areas
401
+ - **no** — document rationale in `context/{id}.md` Discretion Areas (or `context/_shared.md` if cross-cutting)
400
402
  - **later** — rides on the verifying **Interface Testing Gate**: when `interface` is declared in `project.yml`, that gate already BLOCKS at verifying if UI tests are still missing (no separate planning hook needed)
401
403
 
402
404
  Decision captured once, pre-code. Does not block planning.
@@ -472,7 +474,7 @@ Done when approved.
472
474
 
473
475
  ## Handoff
474
476
 
475
- 1. **Persist** -- plans `.forge/phases/`, reqs `.forge/requirements/m{N}.yml`, roadmap `.forge/roadmap.yml`, context `.forge/context.md`
477
+ 1. **Persist** -- plans `.forge/phases/`, reqs `.forge/requirements/m{N}.yml`, roadmap `.forge/roadmap.yml`, context `.forge/context/{id}.md`
476
478
  2. **Reserve version (if delivery bumps the version)** -- if any plan in this milestone will bump `package.json`/the project version: pull first, derive `version` = highest reserved `max()` bumped by this change's semver level, then **claim it in the ledger BEFORE editing releases.yml**: `.claude/hooks/forge-reserve.sh version --value <X.Y.Z> --milestone m-{N}` — a non-zero exit names the sibling milestone already holding that number (its reservation may be worktree-only and invisible in any file you can read): re-read `max()` counting the ledger's version claims and bump again (ADR-029). Then append ONE entry to `.forge/releases.yml` (Version Reservation Protocol, FORGE.md): `{milestone, version, bump, reserved_at, summary}`. **Never edit prior entries.** No version bump in scope → skip. Surface the reserved number to the user.
477
479
  3. **State** -- `current.status` = `executing` in `.forge/state/milestone-{id}.yml`
478
480
  4. **State-sync commit** (State Commit Protocol): `git -C {worktree_path} add .forge/` then `git -C {worktree_path} commit -m "chore(forge): sync state after planning — m{N} {phase-name}"` (worktree form — recorded `lifecycle.worktree_path` is authoritative over cwd; see FORGE.md State Commit Protocol. Scoped; never `git add .`). The `releases.yml` reservation rides in this commit — **push it** so a parallel session sees the number taken before they reserve.
@@ -9,6 +9,8 @@ Gather context before planning. Bad research leads to bad plans.
9
9
 
10
10
  > Entered via a worktree already, for new work (R10, `forge/SKILL.md` Step 3) — research writes `.forge/research/` state, so if this skill is somehow invoked directly on the main checkout for new work, enter one now before writing anything.
11
11
 
12
+ If resuming research on an existing milestone, skim `.forge/context/{id}.md` first (if present) — decisions already locked there are out of scope for research now; don't re-investigate what's already settled.
13
+
12
14
  ## Research Types
13
15
 
14
16
  ### 1. Codebase Research
@@ -312,6 +312,22 @@ Deferred issues triage: Glob `.forge/deferred-issues/*.md` (else legacy `.forge/
312
312
  - **Fix-now** → route to `planning` fix mode before completing milestone
313
313
  - **Dismiss** → set `status: dismissed` + `dismissed_reason` in the issue's file (or the legacy file)
314
314
 
315
+ Close-the-loop triage (advisory — never blocks): catches shipped-but-still-open GitHub issues. Forge produces resolution artifacts (ADRs, desire-paths, CHANGELOG/commits) that cite the issue they resolve, but nobody circles back to close the issue — the open list accumulates already-done work. This step cross-references those artifacts against **open** issues and surfaces likely-resolved ones for the operator to close. **Detect-and-surface only — NEVER auto-close** (closing a GitHub issue is outward-facing → operator confirmation, agent safety rules).
316
+
317
+ 1. **Gate / degrade first.** Resolve `forge.upstream_repo` from `project.yml`. If absent, OR `gh` not on PATH, OR `gh auth status` fails, OR offline → **skip the whole step** (at most one log line: *"close-the-loop: gh unavailable — skipped"*). Never blocks reviewing or completion. (Same posture as `forge/desire-paths-review.md`'s resolve→check-gh→offer and notion-integration's advisory-everywhere.)
318
+ 2. **Collect resolution-intent `#N` refs produced THIS milestone** (milestone-scoped default — cheap, fires when the artifact is fresh):
319
+ - **ADRs** authored/touched this milestone whose `**Context source:**` / `resolves` / `closes` line cites `forge#N` (or the `…/issues/N` URL). **Exclude bare supersession / see-also mentions** — a *"Superseded by ADR-021 ([forge#19](…))"* note references #19, it does not resolve it. Only resolution-intent lines count.
320
+ - **desire-path** files with `resolves_issue: N`.
321
+ - **CHANGELOG / commit** lines landed this milestone with `closes|fixes|resolves #N`.
322
+ Scope by this milestone's git range / this milestone's artifacts — prose-directed grep, no executable.
323
+ 3. **Intersect cited ∩ still-open.** `gh issue view N --json state,title` each; **keep only `state == OPEN`**. An `#N` gh can't resolve (nonexistent / transferred / errors on that call) OR reports CLOSED → **no candidate** (surface only gh-confirmed-open — zero false positives). Skip any `#N` with a `.forge/state/issue-loop/declined/{N}.yml` marker (glob first — mirrors the desire-path `declined/` skip).
324
+ 4. **Surface survivors** (existing triage idiom): *"**Close-the-loop** ({N}): 1. #N '{title}' — looks resolved by {artifact} — close it? [y/N]"*.
325
+ - **y** (or "close") → **do NOT close autonomously.** Print the exact command for the operator to run (or to approve Forge running as an explicit per-action step): `gh issue close N --comment "Resolved by {artifact} ({ref}). Closing via Forge close-the-loop."`
326
+ - **N** (or "still open") → write `.forge/state/issue-loop/declined/{N}.yml` (`{issue: N, declined_at: <ISO date>, reason: "<one line>"}`; create the dir if absent) so it is not re-nagged. The marker is inert once the issue actually closes (gh stops returning it as open).
327
+ 5. **Optional full sweep** (operator-invoked, natural language — no new command): if the operator asks *"run a full close-the-loop sweep"* / *"check for orphaned issues"*, run steps 1–4 over **all** open issues × **all** shipped resolution artifacts repo-wide (not just this milestone), same rules + `declined/` markers. This is the periodic-cleanup path; the milestone-scoped default stays quiet.
328
+
329
+ Zero candidates → silent (the common case). This step is **advisory — it never gates milestone completion** (see Gates).
330
+
315
331
  ## Step 7: Backlog + Route
316
332
 
317
333
  ### Backlog
@@ -371,6 +387,7 @@ Idempotent: a backlog with no terminal items and all-canonical statuses is left
371
387
  - **Security/arch critical** -> soft gate (accept risk)
372
388
  - **Warnings** -> advisory
373
389
  - **Refactoring** -> never block
390
+ - **Close-the-loop** -> advisory, never blocks (detect-and-surface only; never auto-closes)
374
391
 
375
392
  Report = audit trail.
376
393
 
@@ -119,6 +119,25 @@ Rebase now? (each / list-only / skip)
119
119
  - **list-only / skip** → print the commands; the operator runs them at a safe stopping point.
120
120
  - Gitignored carve-outs (`.mcp.json`, experimental hooks) are not carried by a rebase — whenever layer (b) re-synced anything, add: each worktree must re-run `experimental/{pkg}/install.sh`.
121
121
 
122
+ ### e. Primary-checkout weld (`core.hooksPath`)
123
+
124
+ Runs on every upgrade (registry and Dev Mode alike) — the bin syncs the `.forge/git-hooks/` body, but `core.hooksPath` is per-clone git config the bin never touches (issue #21, ADR-031). Wire it with the **detect + warn, never clobber** policy — identical to `initializing` Finalize step 8. Main checkout only (`git rev-parse --git-dir` == `--git-common-dir`; from a worktree, skip — the primary checkout owns the config):
125
+
126
+ ```bash
127
+ want=".forge/git-hooks"
128
+ cur=$(git config --get core.hooksPath 2>/dev/null || true)
129
+ if [ -z "$cur" ] || [ "$cur" = "$want" ]; then
130
+ git config core.hooksPath "$want" # repo-relative → resolves inside every worktree
131
+ echo "[Forge] core.hooksPath → $want (primary-checkout weld active)."
132
+ else
133
+ echo "[Forge] core.hooksPath is already '$cur' (Husky/lefthook?). NOT overwriting — the"
134
+ echo " primary-checkout pre-commit backstop is inactive. Chain .forge/git-hooks/pre-commit"
135
+ echo " into your existing hooks dir to keep the weld, or accept PreToolUse-only coverage."
136
+ fi
137
+ ```
138
+
139
+ Idempotent — re-running an upgrade that already wired it is a silent no-op. **WHY repo-relative + never-clobber:** same reasons as `initializing` step 8 (worktree-resolvable path; a consumer's Husky/lefthook must never be silently disabled).
140
+
122
141
  ## Dev Mode (`/upgrading dev` — explicit only)
123
142
 
124
143
  Local-clone sync for developing Forge itself: test framework changes in a project without publishing to npm. Presence of `.forge/dev-source` NEVER selects this mode — only the explicit `dev` argument does.
@@ -141,7 +160,7 @@ Dev mode syncs from a checkout, so it keeps file-classification semantics — **
141
160
 
142
161
  - **Framework-owned** (`.claude/agents/*.md`, `.claude/skills/*/SKILL.md`, `.forge/FORGE.md`) → overwrite when different. Ownership is per top-level **unit** — a skill DIR under `.claude/skills/`, a flat agent FILE under `.claude/agents/`: a unit the template doesn't ship (user-authored, vendored-parity, or an opt-in experimental skill) is **preserved, never deleted**; only stale files *inside* a still-shipped skill dir are cleaned. Experimental skills additionally get the staleness pass below.
143
162
  - **Template-only** (`.forge/templates/**`, `.forge/migrations/**`, `.forge/adapters/**`, `.forge/gitignore` → `.forge/.gitignore`) → overwrite; `.forge/FORGE.md` lands BEFORE the CLAUDE.md pass.
144
- - **Additive** (`.claude/hooks/**` incl. `tests/`, `.claude/rules/**`, `.forge/checks/**`) → add + overwrite template-shipped files, never delete local extras, `chmod +x` synced `*.sh`.
163
+ - **Additive** (`.claude/hooks/**` incl. `tests/`, `.claude/rules/**`, `.forge/checks/**`, `.forge/git-hooks/**`) → add + overwrite template-shipped files, never delete local extras, `chmod +x` synced `*.sh` (and the extensionless git hooks under `.forge/git-hooks/`).
145
164
  - **Import-managed** `CLAUDE.md` → the bin's `ensureClaudeMdImport()` cases verbatim; user content outside a forge section is never modified.
146
165
  - **Merge-owned** `.claude/settings.json` → `forge.*` keys + additive hook-group wiring only; stamp `forge.version` last.
147
166
  - **Never touch** user-generated files: `.forge/project.yml`, `.forge/state/`, `.forge/constitution.md`, `.forge/context.md`, `.forge/requirements/`, `.forge/roadmap.yml`, `.forge/design-system.md`, `.forge/refactor-backlog.yml`.
@@ -150,11 +169,12 @@ Render the same report shape as the default path, with `Source: {path}` instead
150
169
 
151
170
  ### Post-sync layers
152
171
 
153
- Re-use layers (a)–(d) with dev-derived inputs — this is the one mode that computes them itself, since there is no engine JSON:
172
+ Re-use layers (a)–(e) with dev-derived inputs — this is the one mode that computes them itself, since there is no engine JSON:
154
173
 
155
174
  - **(a) Migrations** — the data-driven Detection loop: select freshly-synced guides `.forge/migrations/{v}-*.md` where `installed < v <= source` (semver on the PRE-stamp installed version), run each guide's `## Detection` bash block from the project root; `MIGRATE` on stdout → the layer-(a) prompt with the text after `MIGRATE —` as the reason; no output → `{v}: no action`; block errors → `{v}: detection error (skipped)`, never abort the loop. Range bookkeeping is implicit: once `forge.version` is stamped, crossed guides fall out of the next run's range — no ledger.
156
175
  - **(b) Experimental staleness** — diff installed non-template skill dirs against `{source}/packages/create-forge/experimental/*/source/skills/{name}/`; stale → the layer-(b) offer with `{source}` as the source path.
157
176
  - **(c) Codex** — presence glob (`.agents/` / `.codex/`) → the layer-(c) offer.
158
177
  - **(d) Worktrees** — `git worktree list --porcelain` filtered to `refs/heads/forge/m-*`, main checkout only (`git rev-parse --git-dir` == `--git-common-dir`; from a worktree, skip and say so) → the layer-(d) offer.
178
+ - **(e) Primary-checkout weld** — wire `core.hooksPath` with the detect+warn+never-clobber policy above (main checkout only). Same behavior in Dev Mode as on the registry path.
159
179
 
160
180
  Same consent gates, same report lines as the registry path.
@@ -19,7 +19,7 @@ After `/clear`, load with fresh eyes — don't carry the executor's assumptions:
19
19
  Read: .forge/state/milestone-{id}.yml → current phase, plans completed
20
20
  Read: .forge/project.yml → tech stack (for running tests)
21
21
  Read: .forge/phases/milestone-{id}/{phase}-{name}/plan-{NN}.md → must_haves (truths, artifacts, key_links)
22
- Read: .forge/context.md → locked decisions
22
+ Read: .forge/context/{id}.md → locked decisions for this milestone
23
23
  Read: .forge/requirements/m{N}.yml → requirement IDs for coverage check (current milestone, resolved from state)
24
24
  Read: .forge/deferred-issues/*.md → known pre-existing failures (glob the dir; else legacy .forge/deferred-issues.md; advisory)
25
25
  ```
@@ -295,7 +295,7 @@ After verification completes (PASSED or GAPS FOUND), run a quick retrospective o
295
295
 
296
296
  **6. User corrections**: User correct the same thing multiple times? Implicit rules that should become explicit.
297
297
 
298
- **Set `scope` on each observation** (see ADR-012): `framework` when the fix targets Forge's own skills/templates (tier heuristics, a skill's step, a template); `project` when it targets user-owned files (`constitution.md`, `design-system.md`, `context.md`). Record `suggestion_target` when known. This classifies the signal at write time so review can route `framework` signals upstream. Tier-override signals (#2) and the "constitution needs a new article" deviation case (#1) are usually `framework` and `project` respectively — judge by the file the fix touches, not the type.
298
+ **Set `scope` on each observation** (see ADR-012): `framework` when the fix targets Forge's own skills/templates (tier heuristics, a skill's step, a template); `project` when it targets user-owned files (`constitution.md`, `design-system.md`, `context/{id}.md`). Record `suggestion_target` when known. This classifies the signal at write time so review can route `framework` signals upstream. Tier-override signals (#2) and the "constitution needs a new article" deviation case (#1) are usually `framework` and `project` respectively — judge by the file the fix touches, not the type.
299
299
 
300
300
  **Surfacing is owned by `forge` boot — not here** (single review owner, ADR-012). `verifying` only *records* observations; it does not surface recommendations, prompt for fixes, or archive. The 3+ aggregation, the type→evolution suggestion table, the decline-ledger, and upstream routing all live in `forge` Step 1.2. Write the observation files (with `scope`) and move on — the next `forge` boot reviews them.
301
301