forge-workflow 0.1.0-beta.3 → 0.1.0-beta.5

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 (196) hide show
  1. package/AGENTS.md +14 -7
  2. package/CHANGELOG.md +43 -1
  3. package/README.md +6 -2
  4. package/bin/forge-cmd.js +21 -1
  5. package/bin/forge.js +16 -369
  6. package/docs/INDEX.md +1 -1
  7. package/docs/guides/BEADS_GITHUB_SYNC.md +2 -31
  8. package/docs/guides/MIGRATION.md +4 -4
  9. package/docs/guides/SETUP.md +16 -16
  10. package/docs/reference/COMMANDS.md +9 -4
  11. package/docs/reference/INSIGHTS_RECAP.md +9 -20
  12. package/docs/reference/RELEASE.md +5 -3
  13. package/docs/reference/TOOLCHAIN.md +8 -0
  14. package/docs/reference/protected-state-surfaces.md +4 -4
  15. package/docs/reference/shepherd.md +117 -17
  16. package/lefthook.yml +12 -0
  17. package/lib/activation/ensure-forge-home.js +33 -15
  18. package/lib/adapters/greptile-review-adapter.js +1 -1
  19. package/lib/adapters/pr-state-adapter.js +397 -100
  20. package/lib/agents-config.js +5 -0
  21. package/lib/audit-evidence.js +71 -110
  22. package/lib/capped-jsonl-log.js +236 -0
  23. package/lib/commands/_issue.js +31 -46
  24. package/lib/commands/_manifest.js +1 -1
  25. package/lib/commands/_registry.js +2 -2
  26. package/lib/commands/_resolve-command-opts.js +36 -29
  27. package/lib/commands/claim.js +2 -4
  28. package/lib/commands/clean.js +196 -32
  29. package/lib/commands/dev.js +4 -33
  30. package/lib/commands/hooks.js +358 -13
  31. package/lib/commands/insights.js +8 -3
  32. package/lib/commands/merge.js +600 -40
  33. package/lib/commands/plan.js +23 -115
  34. package/lib/commands/pr.js +1 -1
  35. package/lib/commands/preflight.js +11 -2
  36. package/lib/commands/prime.js +23 -3
  37. package/lib/commands/push.js +41 -51
  38. package/lib/commands/recall.js +60 -16
  39. package/lib/commands/recap.js +6 -1
  40. package/lib/commands/release.js +18 -4
  41. package/lib/commands/serve.js +5 -2
  42. package/lib/commands/setup.js +191 -95
  43. package/lib/commands/shepherd.js +49 -4
  44. package/lib/commands/ship.js +22 -23
  45. package/lib/commands/skill.js +383 -0
  46. package/lib/commands/status.js +54 -33
  47. package/lib/commands/test.js +56 -34
  48. package/lib/commands/worktree.js +247 -43
  49. package/lib/core/runtime-graph.js +89 -15
  50. package/lib/doc-assertions.js +297 -0
  51. package/lib/existing-tdd-gate.js +253 -0
  52. package/lib/forge-context.js +1 -4
  53. package/lib/forge-issues.js +64 -491
  54. package/lib/git-defaults.js +56 -0
  55. package/lib/harness-capability-matrix.js +5 -5
  56. package/lib/hook-renderer.js +147 -16
  57. package/lib/insights.js +96 -80
  58. package/lib/issue-backend.js +42 -3
  59. package/lib/kernel/backing-issue.js +14 -2
  60. package/lib/kernel/broker.js +44 -0
  61. package/lib/kernel/cli-broker-factory.js +12 -1
  62. package/lib/kernel/close-on-merge.js +154 -0
  63. package/lib/kernel/fs-class.js +42 -25
  64. package/lib/kernel/migrations.js +30 -2
  65. package/lib/kernel/schema.js +35 -0
  66. package/lib/kernel/sqlite-driver.js +292 -18
  67. package/lib/lefthook-wiring.js +21 -1
  68. package/lib/memory/router.js +16 -1
  69. package/lib/memory-digest.js +47 -15
  70. package/lib/memory-recall-events.js +145 -0
  71. package/lib/memory-recall.js +212 -0
  72. package/lib/merge-rules.js +8 -4
  73. package/lib/npm-publish-workflow.js +272 -0
  74. package/lib/orientation.js +371 -49
  75. package/lib/plugin-catalog.js +14 -4
  76. package/lib/pr-bundle.js +9 -6
  77. package/lib/pr-monitor/journal.js +18 -2
  78. package/lib/pr-monitor/reconcile-executor.js +842 -0
  79. package/lib/pr-monitor/reconcile-tick.js +138 -0
  80. package/lib/pr-monitor/reconcile.js +0 -0
  81. package/lib/pr-monitor/render-summary.js +196 -0
  82. package/lib/pr-monitor/shepherd-lease.js +252 -0
  83. package/lib/pr-monitor/watch-lifecycle.js +14 -2
  84. package/lib/pr-pull.js +98 -24
  85. package/lib/pr-shepherd.js +34 -8
  86. package/lib/preflight/gates.js +65 -18
  87. package/lib/preflight/runner.js +5 -0
  88. package/lib/project-memory.js +40 -0
  89. package/lib/protected-state-authority.js +305 -0
  90. package/lib/protected-state-surfaces.js +64 -44
  91. package/lib/release-readiness.js +51 -4
  92. package/lib/rules-sync.js +4 -0
  93. package/lib/runtime-health.js +15 -46
  94. package/lib/shell-utils.js +1 -1
  95. package/lib/skill-eval.js +750 -0
  96. package/lib/skills-sync.js +6 -3
  97. package/lib/smart-merge.js +28 -4
  98. package/lib/status/identity.js +46 -0
  99. package/lib/status/presenter.js +0 -35
  100. package/lib/status/snapshot.js +11 -16
  101. package/lib/symlink-utils.js +74 -26
  102. package/lib/upgrade-safety.js +47 -9
  103. package/lib/using-forge.js +328 -0
  104. package/lib/workflow/enforce-stage.js +5 -5
  105. package/lib/workflow/state-manager.js +23 -23
  106. package/package.json +6 -7
  107. package/rules/using-forge.md +24 -0
  108. package/scripts/doc-asserting-tests.js +158 -0
  109. package/scripts/forge-team/index.sh +0 -5
  110. package/scripts/forge-team/tests/dispatcher.test.sh +1 -1
  111. package/scripts/forge-team/tests/workflow-integration.test.sh +0 -1
  112. package/scripts/lib/behavioral-eval-runner.js +310 -0
  113. package/scripts/lib/behavioral-eval-runtime.js +456 -0
  114. package/scripts/lib/eval-evidence.js +328 -0
  115. package/scripts/lib/eval-runner.js +81 -41
  116. package/scripts/lib/immutable-eval-corpus.js +309 -0
  117. package/scripts/lib/promotion-evidence-loader.js +94 -0
  118. package/scripts/lib/promotion-scorecard.js +314 -0
  119. package/scripts/npm-release-receipt.js +134 -0
  120. package/scripts/process-tree.js +761 -0
  121. package/scripts/protected-state-check.js +47 -22
  122. package/scripts/run-command-eval.js +29 -1
  123. package/scripts/sync-d20-audit.js +172 -0
  124. package/scripts/test-full-suite.js +249 -37
  125. package/scripts/test.js +184 -44
  126. package/skills/claim-safety/SKILL.md +4 -0
  127. package/skills/claim-safety/evals/scorecard.json +41 -0
  128. package/skills/coverage.json +83 -0
  129. package/skills/dev/SKILL.md +4 -0
  130. package/skills/dev/evals/scorecard.json +41 -0
  131. package/skills/gates/SKILL.md +80 -0
  132. package/skills/gates/evals/evals.json +38 -0
  133. package/skills/gates/evals/scorecard.json +41 -0
  134. package/skills/hermes-forge/SKILL.md +1 -0
  135. package/skills/hermes-forge/evals/scorecard.json +41 -0
  136. package/skills/issue-basics/SKILL.md +1 -0
  137. package/skills/issue-basics/evals/scorecard.json +41 -0
  138. package/skills/kernel/SKILL.md +38 -0
  139. package/skills/kernel/evals/scorecard.json +41 -0
  140. package/skills/memory/SKILL.md +16 -1
  141. package/skills/memory/evals/scorecard.json +41 -0
  142. package/skills/parallel-deep-research/SKILL.md +1 -0
  143. package/skills/parallel-deep-research/evals/scorecard.json +41 -0
  144. package/skills/plan/SKILL.md +6 -0
  145. package/skills/plan/evals/scorecard.json +41 -0
  146. package/skills/portability/SKILL.md +47 -0
  147. package/skills/portability/evals/evals.json +34 -0
  148. package/skills/portability/evals/scorecard.json +41 -0
  149. package/skills/research/SKILL.md +1 -0
  150. package/skills/research/evals/scorecard.json +41 -0
  151. package/skills/review/SKILL.md +10 -11
  152. package/skills/review/evals/scorecard.json +41 -0
  153. package/skills/rollback/SKILL.md +5 -11
  154. package/skills/rollback/evals/scorecard.json +41 -0
  155. package/skills/setup/SKILL.md +91 -0
  156. package/skills/setup/evals/evals.json +42 -0
  157. package/skills/setup/evals/scorecard.json +41 -0
  158. package/skills/shepherd/SKILL.md +84 -38
  159. package/skills/shepherd/evals/evals.json +21 -9
  160. package/skills/shepherd/evals/scorecard.json +41 -0
  161. package/skills/ship/SKILL.md +10 -12
  162. package/skills/ship/evals/scorecard.json +41 -0
  163. package/skills/smith/SKILL.md +8 -0
  164. package/skills/smith/evals/scorecard.json +41 -0
  165. package/skills/sonarcloud/SKILL.md +1 -0
  166. package/skills/sonarcloud/evals/scorecard.json +41 -0
  167. package/skills/sonarcloud-analysis/SKILL.md +1 -0
  168. package/skills/sonarcloud-analysis/evals/scorecard.json +41 -0
  169. package/skills/status/SKILL.md +3 -0
  170. package/skills/status/evals/scorecard.json +41 -0
  171. package/skills/triage-ready/SKILL.md +2 -0
  172. package/skills/triage-ready/evals/scorecard.json +41 -0
  173. package/skills/using-forge/SKILL.md +104 -0
  174. package/skills/using-forge/evals/scorecard.json +41 -0
  175. package/skills/validate/SKILL.md +4 -0
  176. package/skills/validate/evals/scorecard.json +41 -0
  177. package/skills/verify/SKILL.md +4 -0
  178. package/skills/verify/evals/scorecard.json +41 -0
  179. package/skills/worktree/SKILL.md +92 -0
  180. package/skills/worktree/evals/evals.json +38 -0
  181. package/skills/worktree/evals/scorecard.json +41 -0
  182. package/lib/adapters/beads-issue-adapter.js +0 -127
  183. package/lib/beads-nudge.js +0 -91
  184. package/lib/beads-setup.js +0 -538
  185. package/lib/beads-sync-scaffold.js +0 -189
  186. package/lib/commands/board.js +0 -64
  187. package/lib/pat-setup.js +0 -207
  188. package/lib/pr-monitor/render-sticky.js +0 -192
  189. package/lib/pr-monitor/upsert-sticky.js +0 -169
  190. package/lib/status/beads-snapshot.js +0 -145
  191. package/scripts/beads-context.sh +0 -577
  192. package/scripts/beads-migrate-to-dolt.sh +0 -7
  193. package/scripts/beads-upgrade-smoke.sh +0 -284
  194. package/scripts/forge-team/lib/dashboard.sh +0 -316
  195. package/scripts/forge-team/tests/dashboard.test.sh +0 -155
  196. package/scripts/lib/beads-migrate-to-dolt.mjs +0 -503
@@ -59,10 +59,16 @@ bunx forge setup --agents claude cursor
59
59
  bunx forge setup --all --quick
60
60
  bunx forge setup --path ./my-project --agents codex --dry-run
61
61
  bunx forge setup --merge smart --agents claude,cursor
62
+ bunx forge setup --agents claude --skills-only
62
63
  ```
63
64
 
64
65
  Use `--agents`, not `--agent`.
65
66
 
67
+ Use `--skills-only` (or its `--no-hooks` alias) when you want the Forge skill
68
+ suite without installing Git hooks, Forge-native hook scripts, or Claude/Cursor
69
+ native hook enforcement. The default `forge setup` behavior is unchanged, and
70
+ the option is safe to re-run.
71
+
66
72
  ## Agent Notes
67
73
 
68
74
  Forge currently supports Claude Code, Codex, and Cursor. Hermes support is planned.
@@ -75,31 +81,25 @@ Exact generated files depend on selected agents and existing repository files. U
75
81
 
76
82
  ## Issue Backend
77
83
 
78
- Forge issue commands (`forge ready`, `forge show`, `forge claim`, `forge create`, `forge close`) use the built-in **kernel** backend by default. No install or initialization is required — a fresh clone can track issues immediately.
84
+ Forge issue commands (`forge ready`, `forge show`, `forge claim`, `forge create`, `forge close`) use the built-in **kernel** backend. No install or initialization is required — a fresh clone can track issues immediately.
79
85
 
80
- ## Beads (Opt-Out Backend)
86
+ ## Migrating From Beads
81
87
 
82
- Beads (`bd`) is an optional opt-out backend for teams that prefer Dolt-backed sync internals. Select it (precedence, highest first) with `--issue-backend beads`, `FORGE_ISSUE_BACKEND=beads`, or `issueBackend: beads` in `.forge/config.yaml`; only then is `bd` required. Prefer the current Beads installer documented by Beads itself and this repo's toolchain docs. On Windows, avoid stale global install examples if they hit EPERM or shim issues; use the PowerShell installer path described in [Toolchain](../reference/TOOLCHAIN.md).
88
+ Beads is no longer a selectable backend: `--issue-backend beads`, `FORGE_ISSUE_BACKEND=beads`, and `issueBackend: beads` are rejected, and `bd` is never required. An existing `.beads` directory is import-only state.
83
89
 
84
- When Beads is selected, health checks:
90
+ Import it once, then use the kernel issue commands:
85
91
 
86
92
  ```bash
87
- bd doctor
88
- bd dolt status
89
- forge sync
93
+ forge migrate --from beads
90
94
  ```
91
95
 
92
- If a feature worktree reports `database "forge" not found on Dolt server`, diagnose in the root checkout before changing issue state. This applies only to the Beads backend.
93
-
94
96
  ## Deprecated GitHub Sync Cleanup
95
97
 
96
- To remove old generated GitHub/Beads sync files from an existing install:
97
-
98
- ```bash
99
- bunx forge setup --sync
100
- ```
101
-
102
- `forge setup --sync` is deprecated. It now removes old generated Beads/GitHub sync files instead of creating new sync workflows. Future GitHub issue sync belongs to Forge Kernel/server authority, not Beads runtime files or metadata commits.
98
+ `forge setup` removes old generated GitHub/Beads sync files from an existing
99
+ install automatically — there is no flag for it. Only files whose content
100
+ matches a known generated template are removed, so files you have edited
101
+ yourself are left alone. Future GitHub issue sync belongs to Forge Kernel/server
102
+ authority, not Beads runtime files or metadata commits.
103
103
 
104
104
  ## Validate Setup
105
105
 
@@ -53,20 +53,25 @@ forge plan "<feature description>"
53
53
  forge dev [--issue-id <id>] [--phase red|green|refactor]
54
54
  forge validate
55
55
  forge ship <feature-slug> [<title>] [--dry-run]
56
- forge status [--full] [--json]
57
- forge prime [--budget N] [--json]
56
+ forge status [-v|--verbose] [--full] [--json]
57
+ forge prime [--budget N] [--json] # deprecated alias for `forge status -v`
58
58
  forge orient [--budget N] [--json]
59
59
  forge recap <issue> [--budget N] [--json]
60
60
  forge recall [query] [--limit N] [--all] [--json]
61
61
  forge remember <note> [--tag <label>]... [--json]
62
+ forge skill for "<situation>" [--json]
63
+ forge skill eval <skill> --full --tier <30|100|300> [--json]
62
64
  ```
63
65
 
64
66
  - `forge plan` creates the implementation plan, kernel issue, and feature branch/worktree from a researched feature description; it prints the created issue id, branch name, and the suggested next command.
65
67
  - `forge dev` runs the TDD development stage with RED/GREEN/REFACTOR phase guidance; `--issue-id` scopes it to a specific kernel issue and `--phase` overrides auto-detection.
66
68
  - `forge validate` runs the validation orchestration pipeline (conflict markers, type check, lint, security, tests) with no arguments — see also `bun run check` under Validation And Packaging.
67
69
  - `forge ship` creates the pull request from validated feature work (wraps `gh pr create`); `--dry-run` previews without creating a PR.
68
- - `forge status` is the one-glance orientation command: where you are, what to run next, and your active work; `--full` also shows blocked/stale/recently-completed issues.
69
- - `forge prime` emits the bounded session-entry orientation envelope agents read at the start of a session.
70
+ - `forge status` is the one-glance orientation command: where you are, what to run next, and your active work; `--full` also shows blocked/stale/recently-completed issues. `-v`/`--verbose` prints the full session-entry briefing instead of the short pulse (git-status-v semantics).
71
+ - `forge status -v` emits the bounded session-entry orientation envelope agents read at the start of a session; it leads with a bounded (<=20-line) **live-state** block — current stage, claimed issue(s), ready-work count, enabled gates/rails, and one progressive-adoption nudge — collected best-effort (never blocks, honest fallbacks) before the deterministic orientation.
72
+ - `forge prime` is a **deprecated** alias for `forge status -v`. It still works and its stdout is byte-identical (session-start hooks consume it as context), but it prints a one-line deprecation notice on stderr.
73
+ - `forge skill for "<situation>"` is the deterministic intent-to-skill router: it reads the canonical skill catalog and prints the best-fit Forge skill(s) plus why, as the reasoning fallback for harnesses without a SessionStart hook that auto-injects the `using-forge` dispatch skill. `--json` emits the machine-readable routing result.
74
+ - `forge skill eval <skill> --full --tier <30|100|300>` runs the controlled four-arm behavioral protocol against the current PR head. It requires a live Kernel issue, an open PR at the exact local HEAD, and an isolated supported runtime. Tier 30 is instrumentation-only, tier 100 is the decision gate, and tier 300 is confirmation. Missing attribution, controls, usage, or reconstructable evidence returns `INCOMPLETE`; the command never authorizes merge.
70
75
  - `forge orient` emits bounded project orientation from deterministic source files (broader than `prime`, still token-budgeted via `--budget`).
71
76
  - `forge recap <issue>` is the issue-scoped counterpart to `forge orient`/`forge prime` — it summarizes a single issue from the same deterministic file assembly instead of the whole project. Requires an issue id; running it with no id (or `--help`) prints usage only.
72
77
  - `forge recall` retrieves project-memory notes from the kernel-backed memory store; omit `query` to list recent notes.
@@ -1,6 +1,6 @@
1
1
  # Insights And Recap
2
2
 
3
- `forge insights` and `forge recap` summarize recurring local workflow evidence from existing Forge and Beads state.
3
+ `forge insights` summarizes recurring local workflow evidence from Kernel and Forge state. `forge recap <issue>` renders a bounded, issue-scoped orientation envelope from the same Kernel issue record.
4
4
 
5
5
  ## Commands
6
6
 
@@ -11,24 +11,24 @@ forge insights --min-count 2 --limit 5
11
11
  forge insights --json
12
12
  forge insights accept <candidate-id> --note "why this is useful"
13
13
  forge insights reject <candidate-id> --note "why this is noise"
14
- forge recap
15
- forge recap --json
14
+ forge recap <issue>
15
+ forge recap <issue> --json
16
16
  ```
17
17
 
18
- `--review-feedback` is a compatibility alias. In this MVP it reads Beads interactions and issue evidence; it does not infer external review-provider comments.
18
+ `--review-feedback` is a compatibility alias. It reads kernel events and issue evidence; it does not infer external review-provider comments.
19
19
 
20
20
  ## Evidence Sources
21
21
 
22
- - `.beads/interactions.jsonl`: field changes and review/close outcome reasons.
23
- - `.beads/issues.jsonl`: tokenized issue titles and descriptions for themes, plus statuses and timestamps for recap context.
22
+ - Kernel events (`kernel_events`, newest first): field changes and review/close outcome reasons. Interactions imported from a legacy Beads store are read here too, as `beads.interaction.<kind>` events.
23
+ - Kernel issues (`forge issue list`): tokenized issue titles and descriptions for themes, plus statuses and timestamps.
24
24
  - `.forge/log.jsonl` and `.forge/audit.log`: optional audit event counts when present.
25
- - Beads-backed typed memory: accept/reject decisions are recorded through `lib/memory/typed-api.js`.
25
+ - Typed memory: accept/reject decisions are recorded through `lib/memory/typed-api.js`.
26
26
 
27
27
  ## What It Can Infer
28
28
 
29
29
  - Repeated local workflow patterns.
30
30
  - Candidate follow-ups based on frequency, source diversity, and evidence count.
31
- - Recent issue activity and review outcome counts.
31
+ - Recent issue activity counts.
32
32
  - Whether history is too sparse for a useful suggestion.
33
33
 
34
34
  ## What It Cannot Infer
@@ -48,16 +48,5 @@ Ranked candidates:
48
48
  Next: Review interaction evidence and consider a local workflow skill only if the pattern is still useful.
49
49
  Limitations:
50
50
  - Insights are local workflow signals, not proof of correctness.
51
- ```
52
-
53
- ```text
54
- Forge recap
55
- Issues: 260 total, 94 open, 166 closed
56
- Review outcomes found: 4
57
- Recent work:
58
- - forge-besw.12: forge insights --review-feedback PoC (Week 1 deliverable) [open]
59
- Insight candidates:
60
- - insight-interaction-status-closed-merged-and-verified: status changed to closed (merged-and-verified)
61
- Limitations:
62
- - Sparse Beads interactions or missing Forge audit logs reduce confidence.
51
+ - Sparse kernel events or missing Forge audit logs reduce confidence.
63
52
  ```
@@ -2,9 +2,9 @@
2
2
 
3
3
  This page documents release readiness. Package publishing still requires the explicit publish step after merge.
4
4
 
5
- ## v0.0.11 Boundary
5
+ ## v0.1.0-beta.5 Boundary
6
6
 
7
- v0.0.11 is the public documentation and positioning package release. The release branch bumps package metadata to `0.0.11`; publish only after the release PR is merged, tagged, and validated.
7
+ v0.1.0-beta.5 is the current prerelease boundary. The release branch declares `0.1.0-beta.5`; publish only after the release PR is merged, tagged, and validated on that exact SHA.
8
8
 
9
9
  Keep these release steps explicit:
10
10
 
@@ -23,6 +23,8 @@ bun run check
23
23
  npm pack --dry-run
24
24
  ```
25
25
 
26
+ The trusted npm workflow is generated through `forge release generate-npm-workflow`; direct workflow edits remain blocked. On a release event, the workflow resolves the tag once, runs the complete supported repository suite on that immutable commit, and allows publication only when the attributable suite receipt, verification checkout, and publish checkout all name the same SHA. Missing or mismatched evidence fails closed.
27
+
26
28
  For docs-heavy changes, also run a Markdown link check if available. If no docs checker exists and adding one would broaden the PR, create a follow-up issue instead.
27
29
 
28
30
  ## Packaging Check
@@ -41,7 +43,7 @@ Release notes should include:
41
43
  - adapter compatibility
42
44
  - DeepWiki refresh checklist
43
45
 
44
- The v0.0.11 release notes live in [CHANGELOG.md](../../CHANGELOG.md).
46
+ The v0.1.0-beta.5 release notes live in [CHANGELOG.md](../../CHANGELOG.md).
45
47
 
46
48
  ## Rollback
47
49
 
@@ -140,6 +140,14 @@ Forge project memory is a **kernel-backed** read model. `lib/project-memory.js`
140
140
 
141
141
  `forge remember` / `forge recall` route through this same `kernel_memories` table (via `lib/memory/router.js`), indexed by **FTS5** for token-AND BM25 recall: a `recall` query does full-text matching, and a no-query `recall` returns the newest notes plus a total count (never a full dump). Because the insights engine also writes `kernel_memories`, what it learns is recallable. Any legacy `.forge/memory/notes.jsonl` is imported once on first use, then retired. The opt-in **`graphiti`** backend is **experimental** — its config/doctor checks ship but the runtime write-through emitter is a fast-follow, so selecting it today still writes the local kernel floor. See [docs/guides/memory-backends.md](../guides/memory-backends.md).
142
142
 
143
+ Claude Code setup and upgrade repair the existing Forge-owned `SessionStart` and `UserPromptSubmit` hook groups without replacing user hooks. `SessionStart` supplies a bounded project-memory digest; each submitted prompt can receive relevant FTS5-ranked memory as `additionalContext`. Other harnesses keep their documented CLI/rule/`AGENTS.md` fallback surfaces—Forge does not claim a native dynamic prompt hook where none is verified.
144
+
145
+ Automatic recall is project-local. Forge derives scope from Git's canonical common directory so linked worktrees share one repository identity without crossing into another repository. Eligibility is applied before ranking: foreign scope, already-seen records, stale suggested records, and validly superseded records are excluded. Confirmed memories are authoritative project context; fresh suggested memories appear only under **Suggested memory — verify before relying**. Suggested memories outside the seven-day freshness window remain available to manual recall but are not injected automatically. User-global `.remember` data is not read because it has no authority-safe canonical project identity.
146
+
147
+ Recall is bounded and fail-open: automatic prompt and session memory each have a 400-token budget, while default manual `forge recall` has a 1,200-token budget. Prompt recall has an internal 4.5-second deadline and SessionStart has a 9-second deadline, keeping them inside the 5-second and 10-second hook limits; an unavailable or slow store injects nothing rather than blocking the agent. Local 1,000-record holdouts enforce an FTS5 p95 of at most 250 ms. Linux CI timing acceptance is not claimed yet.
148
+
149
+ The capped prompt shadow log stores aggregate candidate/injection counts and scores only—never prompt terms, session ids, selected memory ids, or memory bodies. The separate `memory.recall.observed` Kernel event likewise stores bounded outcomes, counts, selected ids, source/trust mixes, token estimates, elapsed time, and harness, with no prompt/query text, memory body/snippet, or correlation fingerprint.
150
+
143
151
  Typed memory helpers in `lib/memory/typed-api.js` add category and provenance conventions on top of the same kernel store; they do not create a new datastore. The supported categories are:
144
152
 
145
153
  | Category | Key prefix | Durable backend |
@@ -8,7 +8,7 @@ The committed manifest is `.forge/protected-paths.yaml`. Runtime enforcement is
8
8
 
9
9
  | Surface | Examples | Required write surface |
10
10
  | --- | --- | --- |
11
- | `beads_state` | `.beads/issues.jsonl`, `.beads/config.yaml` | `bd` or Forge issue commands |
11
+ | `beads_state` | `.beads/issues.jsonl`, `.beads/config.yaml` | `forge migrate --from beads`, then Forge issue commands |
12
12
  | `forge_config` | `.forge/config.yaml`, `.forge/protected-paths.yaml` | Forge config/setup API |
13
13
  | `generated_harness` | `AGENTS.md`, `.claude/skills/`, `.codex/skills/`, `.cursor/rules/` | `forge setup` or harness generator |
14
14
  | `memory_projection` | `docs/sessions/`, `docs/memory/`, `.forge/memory/` | Forge memory projection writer |
@@ -23,13 +23,13 @@ The committed manifest is `.forge/protected-paths.yaml`. Runtime enforcement is
23
23
 
24
24
  - Direct edits, additions, modifications, renames, and deletions of protected files are blocked by `scripts/protected-state-check.js`.
25
25
  - Allowed Forge API writes must declare the matching required surface. For example, a Forge config writer must call the protected writer with `surface: "forge_config"` and `viaForgeApi: true`.
26
- - Forge-owned commands that intentionally stage generated protected changes can set `FORGE_PROTECTED_STATE_ALLOWED_SURFACES` to the comma-separated surfaces they own for that command invocation.
26
+ - Surface-only environment declarations do not authorize protected changes. Forge-owned workflow commands must issue a content-bound Kernel capability for the exact actor, worktree, surface, path, and bytes.
27
27
  - Blocked decisions include actor, path, decision, required surface, reason, and repair hint.
28
- - Audit-ready events use kind `protected_state_write` and are recorded through Beads audit when available.
28
+ - Audit-ready events use kind `protected_state_write` and are appended as JSON lines to `.forge/protected-state-audit.jsonl` (git-ignored, capped at the newest 500 records). This log is visibility-only and never authorizes a protected edit. Forge-owned workflow authorization uses one-time, content-bound Kernel events that the protected-state checker consumes; hand-written JSON cannot grant authority. The audit write remains best-effort for ordinary blocked checks, while the workflow generator rolls back its file transaction if its required audit or Kernel authorization write fails.
29
29
 
30
30
  ## Repair Hint
31
31
 
32
- Every blocked path prints a repair hint. A repair hint is specific guidance such as using `bd update` for `beads_state`, `forge setup` for generated harness files, or the package manager for `lockfiles`.
32
+ Every blocked path prints a repair hint. A repair hint is specific guidance such as importing a legacy store with `forge migrate --from beads` for `beads_state`, `forge setup` for generated harness files, or the package manager for `lockfiles`.
33
33
 
34
34
  Example blocked output shape:
35
35
 
@@ -6,13 +6,65 @@ polling / rerun / escalation loop a human otherwise runs by hand after
6
6
  the pre-merge gate (the embedded documentation-and-handoff gate in `/ship` and `/review`).
7
7
 
8
8
  ```bash
9
- forge shepherd <pr-number>
9
+ forge shepherd daemon # singleton reconcile daemon: owns ALL open PRs
10
+ forge shepherd <pr-number> # one bounded pass over a single PR
10
11
  forge shepherd <pr-number> --auto-rebase # opt-in, default OFF
11
12
  forge shepherd <pr-number> --pull # read-only: WHY it is blocked + what to fix
12
13
  forge shepherd <pr-number> --pull --json # same payload as machine-readable JSON
13
14
  forge shepherd <pr-number> --bundle --json # read-only: the COMPLETE PR-state bundle
15
+ forge shepherd watch <pr-number> # single-PR constant monitor loop
16
+ forge shepherd events <pr-number> --since <seq> # read new journal events back
14
17
  ```
15
18
 
19
+ ## Daemon vs bounded pass (two modes, one verb)
20
+
21
+ There are two ways to run the shepherd, both under the single `forge shepherd`
22
+ verb:
23
+
24
+ - **`forge shepherd daemon` — the singleton reconcile daemon, the default
25
+ ownership model.** It acquires the machine-wide shepherd lease for this repo
26
+ (exiting immediately as a clean no-op if a live daemon already owns it),
27
+ heartbeats, and converges the *entire* PR world every ~60s: self-registering
28
+ hand-opened PRs, restarting killed watchers, reaping verified orphan watchers,
29
+ converging CI check state into kernel verdicts, and retiring merged/closed PRs.
30
+ It **self-retires** — releases the lease, kills its verified children, exits —
31
+ once no PRs remain open. Forge wakes it automatically after a successful
32
+ supported session start, every successful push, and every successful
33
+ non-dry-run ship. These are the only automatic firing seams; ordinary commands
34
+ do not launch it. Once running, an agent does not poll: the daemon owns the
35
+ convergence loop.
36
+ - **`forge shepherd <pr>` — one bounded pass.** Reads one PR's state, takes at
37
+ most one Tier-A action, exits. The point-in-time surface for a single PR (see
38
+ *Bounded-pass model* below).
39
+ - **`forge shepherd watch <pr>` / `watch --adopt`** — foreground streaming watch of
40
+ one PR, or (`--adopt`) adopt every currently-open PR into the watcher set.
41
+ - **`forge shepherd events <pr> --since <seq>`** — the event deltas for a PR since a
42
+ cursor; **`forge shepherd <pr> --pull --json` / `--bundle`** read the kernel verdict
43
+ + rollup without taking any action.
44
+
45
+ Session start automatically wakes the daemon. When an embedding caller supplies
46
+ an executable harness background-shell capability, Forge uses it so the process is reaped with the session;
47
+ bare CLI use falls back to a detached launch from the stable common repository
48
+ root, never a disposable worktree cwd. No liveness check is needed first — the
49
+ O_EXCL singleton lease makes a duplicate start a clean no-op.
50
+
51
+ Embedding boundary:
52
+
53
+ ```js
54
+ hooks.handler(['session-start', '--harness', 'claude'], {}, projectRoot, {
55
+ harness: {
56
+ hasBgShell: true,
57
+ runBgShell: (argv, options) => host.backgroundShell(argv, options),
58
+ },
59
+ });
60
+ ```
61
+
62
+ The host must explicitly provide both `hasBgShell: true` and `runBgShell`;
63
+ Forge never infers this capability from a harness name. The adapter receives
64
+ the stable Git common-root as `options.cwd`; a missing or throwing adapter uses the contained detached fallback.
65
+ (A `forge prime` open-PR + daemon-liveness line is a planned follow-up — W-S5 —
66
+ not yet wired.)
67
+
16
68
  ## `--pull`: the actionable blocker payload
17
69
 
18
70
  `--pull` is a **strictly read-only** signal-gather. It computes the decision
@@ -67,22 +119,23 @@ up from there.
67
119
  a `/loop`) that re-invokes the bounded pass with a debounce of at least 60
68
120
  seconds and cancel-in-progress. The shepherd itself never waits in-process.
69
121
 
70
- ## Auto-start on ship (`rail.auto_shepherd`)
122
+ ## Automatic singleton attachment (`rail.auto_shepherd`)
71
123
 
72
- `forge shepherd watch <pr>` is the constant, self-stopping local monitor loop
73
- (≈60 s jittered cadence; appends events to the per-PR NDJSON journal under
74
- `.forge/pr-monitor/<repo>-<pr>/`; self-stops on `PR_MERGED`/`PR_CLOSED`). On a
75
- successful `forge ship`, the new PR's watcher is **auto-started detached** so a
76
- shipped PR is tended without a manual trigger. The spawn is best-effort and
77
- **never fails ship** (a spawn or config-read error degrades to "not started"),
78
- and it is idempotent — the watch-lifecycle PID/journal lock prevents a second
79
- watcher for the same PR.
124
+ Successful supported session start, push, and non-dry-run ship operations wake the
125
+ repository-wide singleton. Push never requires a locally resolved PR number:
126
+ the daemon owns GitHub enumeration, so even a push with no local open-PR match
127
+ still fires and can adopt open or draft PRs created elsewhere. Push and ship no
128
+ longer start separate per-PR detached watchers.
80
129
 
81
130
  This auto-start is governed by the default-ON, unlocked **`rail.auto_shepherd`**
82
131
  rail. Opt out with `forge gate disable rail.auto_shepherd` (re-enable with
83
- `forge gate enable rail.auto_shepherd`); when disabled, `forge ship` skips the
84
- auto-start. This keeps the behavior honestly toggleable through the same config
85
- surface as every other rail.
132
+ `forge gate enable rail.auto_shepherd`). `FORGE_SHEPHERD_DISABLE=1` is the
133
+ environment kill-switch. CI/test detection is deterministic and injectable via
134
+ the trigger environment: `NODE_ENV=test`, `BUN_ENV=test`, or a set `CI`,
135
+ `GITHUB_ACTIONS`, or `GITLAB_CI` disables automatic fire. These environment
136
+ guards run before repository initialization, lease, Kernel-state, or process
137
+ work; dry-run, uninitialized repositories, and a disabled rail are likewise
138
+ zero-side-effect paths. The manual `forge shepherd` surface remains available.
86
139
 
87
140
  ## Surfacing events back to the agent (`forge hooks shepherd-events`)
88
141
 
@@ -139,17 +192,64 @@ same verb (or `forge shepherd events`) on its own cadence.
139
192
  - **Auth taxonomy.** 401 (expiry) pauses and surfaces; 403 insufficient-scope is
140
193
  a hard-stop; 403 with `Retry-After` honors the delay and resumes next pass.
141
194
 
195
+ ## GitHub Actions backstop — auto-updated heads re-trigger CI (`FORGE_PR_TOKEN`)
196
+
197
+ The `pr-monitor.yml` Actions workflow can auto-update an otherwise-clean-but-behind
198
+ PR branch (merge base into the head). That push must **re-trigger CI on the new
199
+ head**, or the head sits with no required checks running and can never merge.
200
+
201
+ GitHub deliberately does **not** start new workflow runs for events created with
202
+ the default `GITHUB_TOKEN` — a `pull_request: synchronize` it produces lands in an
203
+ *approval-required* state instead of running. So an auto-update authored by
204
+ `GITHUB_TOKEN` leaves a **CI-dead head**. (Official rule:
205
+ <https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/trigger-a-workflow>.)
206
+
207
+ **Fix (maintainer action required):** create a repository secret named
208
+ **`FORGE_PR_TOKEN`** holding a fine-grained **PAT** (or GitHub-App installation
209
+ token). The PAT's OWN permissions — not the workflow's `permissions:` block, which
210
+ only governs the built-in `GITHUB_TOKEN` — must cover every call the step makes:
211
+
212
+ | Permission | Why |
213
+ | --- | --- |
214
+ | **Contents: write** | push the update-branch merge to the head |
215
+ | **Pull requests: write** | `PUT /pulls/{n}/update-branch` (the update-branch API) |
216
+ | **Checks: write** | create the `forge/auto-update` marker check run |
217
+ | **Workflows** | only if the base branch may change `.github/workflows/**` (the merge would carry it) |
218
+
219
+ The auto-update-branch step uses the token and falls back to `GITHUB_TOKEN` when
220
+ the secret is absent:
221
+
222
+ ```yaml
223
+ GH_TOKEN: ${{ secrets.FORGE_PR_TOKEN || github.token }}
224
+ ```
225
+
226
+ With the secret set, auto-updated heads re-run CI automatically. Without it, the
227
+ workflow behaves exactly as before (no regression) — it just cannot auto-run CI on
228
+ the updated head. Forge only wires the code path and reads the secret; **creating
229
+ the secret is the maintainer's responsibility** — Forge never fabricates a token.
230
+
231
+ ## GitHub Actions PR-monitor summary
232
+
233
+ The repository PR-monitor workflow writes detailed review-thread and check
234
+ diagnostics to the Actions job summary (`GITHUB_STEP_SUMMARY`) and keeps the
235
+ machine-readable view available through `forge shepherd <pr> --pull --json`.
236
+ It projects that same canonical verdict onto exactly one `pr-verdict:*` label
237
+ for a cheap agent-agnostic read. The label is visibility only: merge authority
238
+ continues to use live protected required checks, the current head, and
239
+ unresolved review threads. The workflow does not create PR comments or a
240
+ neutral `forge/pr-monitor` check.
241
+
142
242
  ## Per-harness behavior
143
243
 
144
- - **Claude Code / Codex:** invoke `forge shepherd <pr>` directly; an external
145
- scheduler may drive repeated bounded passes.
244
+ - **Claude Code / Codex:** automatic session-start attachment wakes the singleton;
245
+ invoke `forge shepherd <pr>` directly for a bounded point-in-time read.
146
246
  - **Cursor:** manually-invoked only — run it from a terminal. No polling-loop
147
247
  affordance and no hook reliance on this surface.
148
248
 
149
249
  ## State
150
250
 
151
- Progress is durable in GitHub PR comments and labels plus `git`. The one local
152
- store is the constant monitor's per-PR journal under
251
+ Progress is durable in the Actions job summary, the single verdict label, and
252
+ `git`. The one local store is the constant monitor's per-PR journal under
153
253
  `.forge/pr-monitor/<repo>-<pr>/` (the append-only `events.ndjson` + snapshot and
154
254
  consumer cursors) — the delivery/replay surface for `forge shepherd watch` and
155
255
  `events --since`. The bounded shepherd pass itself keeps no separate local state.
package/lefthook.yml CHANGED
@@ -21,6 +21,14 @@ pre-commit:
21
21
  stage_fixed: false
22
22
  tags: skills
23
23
  glob: "skills/**"
24
+ # Keep the D20 Beads call-site kill-list current with the census it audits.
25
+ # Self-gating: the script exits early unless a staged path actually counts toward
26
+ # the census, because those scan roots are resolved at run time (plugin manifests +
27
+ # .forge/sync-manifest.json) and a static glob here would drift from the gate.
28
+ sync-d20-audit:
29
+ run: node scripts/sync-d20-audit.js
30
+ stage_fixed: false
31
+ tags: release
24
32
  protected-state:
25
33
  run: node scripts/protected-state-check.js
26
34
  stage_fixed: false
@@ -50,6 +58,10 @@ pre-push:
50
58
  - run: node scripts/check-forge-token.js
51
59
 
52
60
  # 3. Test suite (cross-platform Node.js script detects package manager)
61
+ # `forge push --quick` sets FORGE_PUSH_LANE=quick on the git push it spawns;
62
+ # scripts/test.js honors that declaration and skips this job with a loud
63
+ # notice so the quick lane is lint-only end to end. Only this job reads it —
64
+ # branch protection and lint above always run, and CI runs the full matrix.
53
65
  tests:
54
66
  run: node scripts/test.js
55
67
  tags: tests
@@ -11,11 +11,11 @@
11
11
  * bare repo stays untouched until the user actually changes state.
12
12
  *
13
13
  * "Bare-minimum" here is strictly LESS than `forge init --minimal`: this writes
14
- * ONLY `.forge/config.yaml` with every gate disabled (the `minimal` adoption
15
- * profile). It installs NO git hooks, NO lefthook.yml, NO protected-paths
16
- * manifest, NO `.mcp.json`, and NO scripts tree — those remain the opt-in
17
- * payload of `forge setup`, never forced. Progressive growth: heavier slices
18
- * initialize themselves on first use.
14
+ * ONLY `.forge/config.yaml` with Forge's canonical default enforcement posture.
15
+ * It installs NO git hooks, NO lefthook.yml, NO protected-paths manifest, NO
16
+ * `.mcp.json`, and NO scripts tree — those remain the opt-in payload of
17
+ * `forge setup`, never forced. Progressive growth: heavier slices initialize
18
+ * themselves on first use.
19
19
  *
20
20
  * @module activation/ensure-forge-home
21
21
  */
@@ -78,26 +78,33 @@ function isMutatingVerb(name, command) {
78
78
  }
79
79
 
80
80
  /**
81
- * Render the bare-minimum, gates-disabled config body.
81
+ * Render the bare-minimum config body with Forge's default enforcement posture.
82
82
  *
83
- * Reuses the canonical `minimal` adoption profile so the produced YAML is
84
- * schema-valid and identical in spirit to `forge init --minimal` (minus the
85
- * hooks/protected-paths side effects). Overridable via `deps.renderConfig` for
86
- * tests that don't want to depend on the profile renderer.
83
+ * Reuses the canonical `standard` adoption profile so the produced YAML is
84
+ * schema-valid and keeps default gates enabled. Overridable via
85
+ * `deps.renderConfig` for tests that don't want to depend on the profile
86
+ * renderer.
87
87
  *
88
88
  * @param {object} [deps]
89
89
  * @returns {string} config.yaml contents
90
90
  */
91
- function renderMinimalConfig(deps = {}) {
92
- const render = deps.renderConfig || (() => renderAdoptionConfigYaml('minimal'));
91
+ function renderDefaultConfig(deps = {}) {
92
+ const render = deps.renderConfig || (() => renderAdoptionConfigYaml('standard'));
93
93
  return render();
94
94
  }
95
95
 
96
+ /**
97
+ * Backward-compatible export name for callers of the previously exported
98
+ * helper. New code should use `renderDefaultConfig`; lazy initialization no
99
+ * longer creates a minimal/disabled configuration.
100
+ */
101
+ const renderMinimalConfig = renderDefaultConfig;
102
+
96
103
  /**
97
104
  * Idempotently create the bare-minimum `.forge/` skeleton for a mutating verb.
98
105
  *
99
- * No-clobber by construction: if `.forge/` already exists (inited repo, or a
100
- * partially-created home), this is a NO-OP and never touches the user's files.
106
+ * No-clobber by construction: an existing config is a NO-OP. A partial home
107
+ * whose `.forge/` directory exists without `config.yaml` is completed safely.
101
108
  * The kernel/issue store (`.forge/kernel/…`) is created lazily by the broker on
102
109
  * the mutating verb itself — this function only guarantees the config skeleton.
103
110
  *
@@ -123,13 +130,24 @@ function ensureForgeHome(projectRoot = process.cwd(), deps = {}) {
123
130
  }
124
131
 
125
132
  fsImpl.mkdirSync(forgeDir, { recursive: true });
126
- fsImpl.writeFileSync(configPath, renderMinimalConfig(deps), 'utf8');
133
+ try {
134
+ fsImpl.writeFileSync(configPath, renderDefaultConfig(deps), {
135
+ encoding: 'utf8',
136
+ flag: 'wx',
137
+ });
138
+ } catch (error) {
139
+ if (error?.code === 'EEXIST') {
140
+ return { created: false, reason: 'config-exists', configPath };
141
+ }
142
+ throw error;
143
+ }
127
144
  return { created: true, configPath };
128
145
  }
129
146
 
130
147
  module.exports = {
131
148
  ensureForgeHome,
132
149
  isMutatingVerb,
150
+ renderDefaultConfig,
133
151
  renderMinimalConfig,
134
152
  MUTATING_VERBS,
135
153
  };
@@ -86,7 +86,7 @@ function matchThreadsToCommitsWithGit(threads, projectRoot, opts = {}) {
86
86
  }
87
87
 
88
88
  const exec = opts._exec || ((cmd, args) => {
89
- return execFileSync(cmd, args, { encoding: 'utf8' });
89
+ return execFileSync(cmd, args, { encoding: 'utf8', windowsHide: true });
90
90
  });
91
91
 
92
92
  let sinceCommit = opts.sinceCommit;