forge-workflow 0.1.0-beta.4 → 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.
- package/AGENTS.md +14 -7
- package/CHANGELOG.md +43 -1
- package/README.md +6 -2
- package/bin/forge-cmd.js +20 -0
- package/bin/forge.js +16 -374
- package/docs/INDEX.md +1 -1
- package/docs/guides/BEADS_GITHUB_SYNC.md +2 -31
- package/docs/guides/MIGRATION.md +4 -4
- package/docs/guides/SETUP.md +16 -16
- package/docs/reference/COMMANDS.md +8 -5
- package/docs/reference/INSIGHTS_RECAP.md +9 -20
- package/docs/reference/RELEASE.md +5 -3
- package/docs/reference/TOOLCHAIN.md +8 -0
- package/docs/reference/protected-state-surfaces.md +4 -4
- package/docs/reference/shepherd.md +54 -25
- package/lefthook.yml +12 -0
- package/lib/activation/ensure-forge-home.js +33 -15
- package/lib/adapters/pr-state-adapter.js +344 -142
- package/lib/audit-evidence.js +71 -110
- package/lib/capped-jsonl-log.js +236 -0
- package/lib/commands/_registry.js +2 -2
- package/lib/commands/clean.js +196 -32
- package/lib/commands/dev.js +4 -33
- package/lib/commands/hooks.js +223 -25
- package/lib/commands/insights.js +8 -3
- package/lib/commands/merge.js +600 -40
- package/lib/commands/pr.js +1 -1
- package/lib/commands/preflight.js +11 -2
- package/lib/commands/prime.js +21 -8
- package/lib/commands/push.js +41 -51
- package/lib/commands/recall.js +60 -16
- package/lib/commands/recap.js +6 -1
- package/lib/commands/release.js +17 -2
- package/lib/commands/setup.js +191 -94
- package/lib/commands/shepherd.js +13 -1
- package/lib/commands/ship.js +22 -23
- package/lib/commands/skill.js +119 -11
- package/lib/commands/status.js +17 -1
- package/lib/commands/test.js +24 -34
- package/lib/commands/worktree.js +220 -42
- package/lib/core/runtime-graph.js +1 -1
- package/lib/doc-assertions.js +297 -0
- package/lib/existing-tdd-gate.js +253 -0
- package/lib/forge-context.js +1 -4
- package/lib/forge-issues.js +56 -32
- package/lib/git-defaults.js +56 -0
- package/lib/harness-capability-matrix.js +3 -3
- package/lib/hook-renderer.js +93 -4
- package/lib/insights.js +96 -80
- package/lib/kernel/backing-issue.js +14 -2
- package/lib/kernel/broker.js +16 -0
- package/lib/kernel/cli-broker-factory.js +12 -1
- package/lib/kernel/close-on-merge.js +154 -0
- package/lib/kernel/fs-class.js +42 -25
- package/lib/kernel/sqlite-driver.js +153 -29
- package/lib/lefthook-wiring.js +21 -1
- package/lib/memory/router.js +16 -1
- package/lib/memory-digest.js +47 -15
- package/lib/memory-recall-events.js +145 -0
- package/lib/memory-recall.js +71 -10
- package/lib/merge-rules.js +8 -4
- package/lib/npm-publish-workflow.js +272 -0
- package/lib/orientation.js +68 -43
- package/lib/plugin-catalog.js +14 -4
- package/lib/pr-bundle.js +5 -6
- package/lib/pr-monitor/journal.js +18 -2
- package/lib/pr-monitor/reconcile-executor.js +224 -41
- package/lib/pr-monitor/render-summary.js +196 -0
- package/lib/pr-monitor/shepherd-lease.js +10 -1
- package/lib/pr-monitor/watch-lifecycle.js +13 -1
- package/lib/pr-pull.js +33 -14
- package/lib/pr-shepherd.js +34 -8
- package/lib/preflight/gates.js +65 -18
- package/lib/preflight/runner.js +5 -0
- package/lib/project-memory.js +33 -1
- package/lib/protected-state-authority.js +305 -0
- package/lib/protected-state-surfaces.js +64 -44
- package/lib/release-readiness.js +51 -4
- package/lib/shell-utils.js +1 -1
- package/lib/skills-sync.js +6 -3
- package/lib/smart-merge.js +28 -4
- package/lib/symlink-utils.js +74 -26
- package/lib/upgrade-safety.js +39 -0
- package/lib/using-forge.js +19 -6
- package/package.json +6 -7
- package/scripts/doc-asserting-tests.js +158 -0
- package/scripts/lib/behavioral-eval-runner.js +310 -0
- package/scripts/lib/behavioral-eval-runtime.js +456 -0
- package/scripts/lib/eval-evidence.js +328 -0
- package/scripts/lib/eval-runner.js +81 -41
- package/scripts/lib/immutable-eval-corpus.js +309 -0
- package/scripts/lib/promotion-evidence-loader.js +94 -0
- package/scripts/lib/promotion-scorecard.js +314 -0
- package/scripts/npm-release-receipt.js +134 -0
- package/scripts/process-tree.js +761 -0
- package/scripts/protected-state-check.js +47 -22
- package/scripts/run-command-eval.js +29 -1
- package/scripts/sync-d20-audit.js +172 -0
- package/scripts/test-full-suite.js +249 -37
- package/scripts/test.js +176 -43
- package/skills/review/SKILL.md +4 -11
- package/skills/review/evals/scorecard.json +3 -3
- package/skills/rollback/SKILL.md +4 -11
- package/skills/rollback/evals/scorecard.json +3 -3
- package/skills/shepherd/SKILL.md +20 -14
- package/skills/shepherd/evals/scorecard.json +2 -2
- package/skills/ship/SKILL.md +4 -12
- package/skills/ship/evals/scorecard.json +3 -3
- package/skills/worktree/SKILL.md +6 -1
- package/skills/worktree/evals/scorecard.json +2 -2
- package/lib/beads-setup.js +0 -538
- package/lib/beads-sync-scaffold.js +0 -189
- package/lib/pat-setup.js +0 -207
- package/lib/pr-monitor/render-sticky.js +0 -206
- package/lib/pr-monitor/upsert-sticky.js +0 -169
- package/scripts/beads-context.sh +0 -577
- package/scripts/beads-migrate-to-dolt.sh +0 -7
- package/scripts/beads-upgrade-smoke.sh +0 -284
- package/scripts/lib/beads-migrate-to-dolt.mjs +0 -503
package/docs/guides/SETUP.md
CHANGED
|
@@ -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
|
|
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
|
-
##
|
|
86
|
+
## Migrating From Beads
|
|
81
87
|
|
|
82
|
-
Beads
|
|
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
|
-
|
|
90
|
+
Import it once, then use the kernel issue commands:
|
|
85
91
|
|
|
86
92
|
```bash
|
|
87
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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,22 +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
62
|
forge skill for "<situation>" [--json]
|
|
63
|
+
forge skill eval <skill> --full --tier <30|100|300> [--json]
|
|
63
64
|
```
|
|
64
65
|
|
|
65
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.
|
|
66
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.
|
|
67
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.
|
|
68
69
|
- `forge ship` creates the pull request from validated feature work (wraps `gh pr create`); `--dry-run` previews without creating a PR.
|
|
69
|
-
- `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.
|
|
70
|
-
- `forge
|
|
71
|
-
- `forge
|
|
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.
|
|
72
75
|
- `forge orient` emits bounded project orientation from deterministic source files (broader than `prime`, still token-budgeted via `--budget`).
|
|
73
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.
|
|
74
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
|
|
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.
|
|
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
|
-
-
|
|
23
|
-
-
|
|
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
|
-
-
|
|
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
|
|
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.
|
|
5
|
+
## v0.1.0-beta.5 Boundary
|
|
6
6
|
|
|
7
|
-
v0.0.
|
|
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.
|
|
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` | `
|
|
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
|
-
-
|
|
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
|
|
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
|
|
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
|
|
|
@@ -21,18 +21,18 @@ forge shepherd events <pr-number> --since <seq> # read new journal events back
|
|
|
21
21
|
There are two ways to run the shepherd, both under the single `forge shepherd`
|
|
22
22
|
verb:
|
|
23
23
|
|
|
24
|
-
- **`forge shepherd daemon` — the singleton reconcile daemon
|
|
24
|
+
- **`forge shepherd daemon` — the singleton reconcile daemon, the default
|
|
25
25
|
ownership model.** It acquires the machine-wide shepherd lease for this repo
|
|
26
26
|
(exiting immediately as a clean no-op if a live daemon already owns it),
|
|
27
27
|
heartbeats, and converges the *entire* PR world every ~60s: self-registering
|
|
28
28
|
hand-opened PRs, restarting killed watchers, reaping verified orphan watchers,
|
|
29
29
|
converging CI check state into kernel verdicts, and retiring merged/closed PRs.
|
|
30
30
|
It **self-retires** — releases the lease, kills its verified children, exits —
|
|
31
|
-
once no PRs remain open.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
36
|
- **`forge shepherd <pr>` — one bounded pass.** Reads one PR's state, takes at
|
|
37
37
|
most one Tier-A action, exits. The point-in-time surface for a single PR (see
|
|
38
38
|
*Bounded-pass model* below).
|
|
@@ -42,9 +42,26 @@ verb:
|
|
|
42
42
|
cursor; **`forge shepherd <pr> --pull --json` / `--bundle`** read the kernel verdict
|
|
43
43
|
+ rollup without taking any action.
|
|
44
44
|
|
|
45
|
-
Session start
|
|
46
|
-
harness background
|
|
47
|
-
|
|
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.
|
|
48
65
|
(A `forge prime` open-PR + daemon-liveness line is a planned follow-up — W-S5 —
|
|
49
66
|
not yet wired.)
|
|
50
67
|
|
|
@@ -102,22 +119,23 @@ up from there.
|
|
|
102
119
|
a `/loop`) that re-invokes the bounded pass with a debounce of at least 60
|
|
103
120
|
seconds and cancel-in-progress. The shepherd itself never waits in-process.
|
|
104
121
|
|
|
105
|
-
##
|
|
122
|
+
## Automatic singleton attachment (`rail.auto_shepherd`)
|
|
106
123
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
**never fails ship** (a spawn or config-read error degrades to "not started"),
|
|
113
|
-
and it is idempotent — the watch-lifecycle PID/journal lock prevents a second
|
|
114
|
-
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.
|
|
115
129
|
|
|
116
130
|
This auto-start is governed by the default-ON, unlocked **`rail.auto_shepherd`**
|
|
117
131
|
rail. Opt out with `forge gate disable rail.auto_shepherd` (re-enable with
|
|
118
|
-
`forge gate enable rail.auto_shepherd`)
|
|
119
|
-
|
|
120
|
-
|
|
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.
|
|
121
139
|
|
|
122
140
|
## Surfacing events back to the agent (`forge hooks shepherd-events`)
|
|
123
141
|
|
|
@@ -210,17 +228,28 @@ workflow behaves exactly as before (no regression) — it just cannot auto-run C
|
|
|
210
228
|
the updated head. Forge only wires the code path and reads the secret; **creating
|
|
211
229
|
the secret is the maintainer's responsibility** — Forge never fabricates a token.
|
|
212
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
|
+
|
|
213
242
|
## Per-harness behavior
|
|
214
243
|
|
|
215
|
-
- **Claude Code / Codex:**
|
|
216
|
-
|
|
244
|
+
- **Claude Code / Codex:** automatic session-start attachment wakes the singleton;
|
|
245
|
+
invoke `forge shepherd <pr>` directly for a bounded point-in-time read.
|
|
217
246
|
- **Cursor:** manually-invoked only — run it from a terminal. No polling-loop
|
|
218
247
|
affordance and no hook reliance on this surface.
|
|
219
248
|
|
|
220
249
|
## State
|
|
221
250
|
|
|
222
|
-
Progress is durable in
|
|
223
|
-
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
|
|
224
253
|
`.forge/pr-monitor/<repo>-<pr>/` (the append-only `events.ndjson` + snapshot and
|
|
225
254
|
consumer cursors) — the delivery/replay surface for `forge shepherd watch` and
|
|
226
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
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
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
|
|
81
|
+
* Render the bare-minimum config body with Forge's default enforcement posture.
|
|
82
82
|
*
|
|
83
|
-
* Reuses the canonical `
|
|
84
|
-
* schema-valid and
|
|
85
|
-
*
|
|
86
|
-
*
|
|
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
|
|
92
|
-
const render = deps.renderConfig || (() => renderAdoptionConfigYaml('
|
|
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:
|
|
100
|
-
*
|
|
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
|
-
|
|
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
|
};
|