forge-workflow 0.1.0-beta.5 → 0.1.0-beta.6
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 +4 -0
- package/CHANGELOG.md +36 -0
- package/CLAUDE.md +0 -12
- package/CODING_STANDARDS.md +72 -0
- package/bin/forge.js +12 -1
- package/docs/guides/MIGRATION.md +3 -3
- package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +4 -0
- package/docs/reference/INSTALL.md +4 -0
- package/docs/reference/LEGACY_CLAIM_REPAIR.md +112 -0
- package/docs/reference/RELEASE.md +4 -4
- package/docs/reference/github-accounts.md +134 -0
- package/docs/reference/shepherd.md +63 -13
- package/lib/adapters/pr-state-adapter.js +15 -2
- package/lib/base-remote.js +138 -0
- package/lib/beta5-compatibility-evidence.js +1093 -0
- package/lib/bun-lockfile-proof.js +413 -0
- package/lib/bun-workflow-pins.js +461 -0
- package/lib/capabilities/index.js +9 -0
- package/lib/capabilities/model.js +141 -0
- package/lib/capabilities/probes.js +347 -0
- package/lib/codex-skills.js +2 -2
- package/lib/commands/_manifest.js +1 -0
- package/lib/commands/_registry.js +48 -18
- package/lib/commands/clean.js +57 -1
- package/lib/commands/doctor.js +37 -6
- package/lib/commands/gate.js +197 -27
- package/lib/commands/github.js +215 -0
- package/lib/commands/hooks.js +54 -6
- package/lib/commands/memory.js +66 -2
- package/lib/commands/merge.js +720 -73
- package/lib/commands/plan.js +33 -2
- package/lib/commands/pr.js +2 -0
- package/lib/commands/preflight.js +10 -2
- package/lib/commands/push.js +108 -6
- package/lib/commands/recall.js +95 -61
- package/lib/commands/release.js +23 -2
- package/lib/commands/remember.js +28 -4
- package/lib/commands/serve.js +26 -9
- package/lib/commands/setup.js +132 -4
- package/lib/commands/shepherd.js +578 -72
- package/lib/commands/ship.js +15 -69
- package/lib/commands/skill.js +8 -0
- package/lib/commands/team.js +47 -8
- package/lib/commands/test.js +163 -4
- package/lib/commands/validate.js +65 -21
- package/lib/commands/worktree.js +155 -19
- package/lib/fixtures/beta5-corpus/v1/README.md +9 -0
- package/lib/fixtures/beta5-corpus/v1/contract/command-contract.json +26 -0
- package/lib/fixtures/beta5-corpus/v1/contract/package-contract.json +13 -0
- package/lib/fixtures/beta5-corpus/v1/contract/workflow-stage-matrix.json +8 -0
- package/lib/fixtures/beta5-corpus/v1/manifest.json +25 -0
- package/lib/fixtures/beta5-corpus/v1/state/comments.jsonl +1 -0
- package/lib/fixtures/beta5-corpus/v1/state/config.yaml +6 -0
- package/lib/fixtures/beta5-corpus/v1/state/dependencies.jsonl +1 -0
- package/lib/fixtures/beta5-corpus/v1/state/issues.jsonl +2 -0
- package/lib/fixtures/beta5-corpus/v1/state/kernel.sql +20 -0
- package/lib/forge-issues.js +78 -0
- package/lib/gate-events.js +98 -10
- package/lib/github-context.js +308 -0
- package/lib/global-flags.js +1 -0
- package/lib/hook-renderer.js +29 -1
- package/lib/issue-render.js +19 -0
- package/lib/kernel/broker.js +723 -31
- package/lib/kernel/claim-reconciler.js +238 -0
- package/lib/kernel/lease-enforcer.js +9 -4
- package/lib/kernel/legacy-claim-repair.js +442 -0
- package/lib/kernel/live-claim-projection.js +26 -0
- package/lib/kernel/migrations.js +118 -3
- package/lib/kernel/readiness-model.js +184 -12
- package/lib/kernel/schema.js +49 -1
- package/lib/kernel/sqlite-driver.js +3322 -183
- package/lib/kernel/taxonomy-validator.js +4 -1
- package/lib/kernel/windows-private-acl.js +239 -0
- package/lib/memory/hygiene.js +191 -0
- package/lib/memory/router.js +94 -27
- package/lib/memory/usage-evidence.js +4 -0
- package/lib/memory-digest.js +59 -0
- package/lib/merge-rules.js +135 -17
- package/lib/npm-publish-workflow.js +233 -40
- package/lib/package-root.js +2 -0
- package/lib/pr-monitor/auto-actions.js +169 -28
- package/lib/pr-monitor/differ.js +110 -4
- package/lib/pr-monitor/events.js +0 -0
- package/lib/pr-monitor/flow-monitor.js +1424 -0
- package/lib/pr-monitor/gather.js +251 -44
- package/lib/pr-monitor/journal.js +0 -37
- package/lib/pr-monitor/monitor.js +117 -10
- package/lib/pr-monitor/process-identity.js +117 -0
- package/lib/pr-monitor/reconcile-executor.js +1101 -625
- package/lib/pr-monitor/reconcile.js +0 -0
- package/lib/pr-monitor/render-summary.js +121 -24
- package/lib/pr-monitor/review-preflight.js +269 -0
- package/lib/pr-monitor/shepherd-lease.js +28 -19
- package/lib/pr-monitor/verdict.js +438 -0
- package/lib/pr-monitor/watch-lifecycle.js +144 -38
- package/lib/pr-monitor/watch-owner.js +1414 -0
- package/lib/pr-monitor/watch.js +129 -58
- package/lib/pr-shepherd.js +17 -3
- package/lib/project-memory.js +145 -3
- package/lib/protected-state-authority.js +799 -4
- package/lib/protected-state-surfaces.js +181 -3
- package/lib/release-readiness.js +2 -3
- package/lib/review-adapter.js +65 -0
- package/lib/skills-sync.js +65 -32
- package/lib/validation/risk-manifest.js +339 -0
- package/lib/workflow/enforce-stage.js +44 -0
- package/lib/workflow/plan-authority.js +225 -0
- package/package.json +8 -4
- package/scripts/commitlint.js +13 -15
- package/scripts/generate-risk-manifest.js +91 -0
- package/scripts/github-context-bridge.sh +10 -0
- package/scripts/legacy-claim-repair.js +145 -0
- package/scripts/lib/behavioral-eval-runtime.js +3 -2
- package/scripts/process-tree.js +14 -2
- package/scripts/protected-state-check.js +440 -17
- package/scripts/sync-agent-skills.js +333 -34
- package/scripts/test-full-suite.js +704 -18
- package/scripts/test-profile.js +13 -3
- package/scripts/test.js +95 -14
- package/skills/coverage.json +1 -0
- package/skills/review/SKILL.md +2 -0
- package/skills/review/evals/scorecard.json +2 -2
- package/skills/setup/SKILL.md +18 -0
- package/skills/setup/evals/scorecard.json +3 -3
- package/skills/shepherd/SKILL.md +19 -2
- package/skills/shepherd/evals/scorecard.json +3 -3
- package/skills/validate/SKILL.md +3 -0
- package/skills/validate/evals/scorecard.json +1 -1
package/scripts/test-profile.js
CHANGED
|
@@ -159,6 +159,17 @@ function finalizeSignalMetrics(metrics, fileDurations) {
|
|
|
159
159
|
};
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
function parseJUnitTestcases(content) {
|
|
163
|
+
if (typeof content !== 'string') return [];
|
|
164
|
+
return Array.from(
|
|
165
|
+
content.matchAll(/<testcase\b([^>]*?)(?:\/>|>([\s\S]*?)<\/testcase>)/g),
|
|
166
|
+
(match) => ({
|
|
167
|
+
attrs: parseAttributes(match[1] || ''),
|
|
168
|
+
body: match[2] || '',
|
|
169
|
+
}),
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
162
173
|
function parseJUnitFiles(files) {
|
|
163
174
|
const fileDurations = new Map();
|
|
164
175
|
const timedOutFiles = new Set();
|
|
@@ -177,9 +188,7 @@ function parseJUnitFiles(files) {
|
|
|
177
188
|
suiteDurationMs += Math.round(Number.parseFloat(attrs.time || '0') * 1000);
|
|
178
189
|
}
|
|
179
190
|
|
|
180
|
-
for (const
|
|
181
|
-
const attrs = parseAttributes(caseMatch[1] || caseMatch[3] || '');
|
|
182
|
-
const body = caseMatch[2] || '';
|
|
191
|
+
for (const { attrs, body } of parseJUnitTestcases(content)) {
|
|
183
192
|
const durationMs = Math.round(Number.parseFloat(attrs.time || '0') * 1000);
|
|
184
193
|
const fallbackFile = path.basename(file);
|
|
185
194
|
const testcase = classifyTestcase({
|
|
@@ -273,6 +282,7 @@ module.exports = {
|
|
|
273
282
|
parseArgs,
|
|
274
283
|
parseAttributes,
|
|
275
284
|
parseJUnitFiles,
|
|
285
|
+
parseJUnitTestcases,
|
|
276
286
|
selectPrimaryBucket,
|
|
277
287
|
walk,
|
|
278
288
|
};
|
package/scripts/test.js
CHANGED
|
@@ -23,11 +23,13 @@ const fs = require('node:fs');
|
|
|
23
23
|
const {
|
|
24
24
|
getAffectedTestFiles,
|
|
25
25
|
getChangedFiles,
|
|
26
|
+
isFullSuiteRequiredFile,
|
|
26
27
|
} = require('../lib/commands/test');
|
|
27
28
|
const { createProcessTree, signalExitCode } = require('./process-tree');
|
|
28
29
|
|
|
29
30
|
const PACKAGE_LEVEL_PATHS = new Set([
|
|
30
31
|
'package.json',
|
|
32
|
+
'bun.lock',
|
|
31
33
|
'bun.lockb',
|
|
32
34
|
'pnpm-lock.yaml',
|
|
33
35
|
'yarn.lock',
|
|
@@ -41,8 +43,49 @@ const KNOWN_TARGETABLE_PREFIXES = [
|
|
|
41
43
|
'.github/agentic-workflows/',
|
|
42
44
|
'.github/workflows/',
|
|
43
45
|
'test/',
|
|
46
|
+
// Source trees with a dedicated suite; see PREFIX_TEST_TARGETS in
|
|
47
|
+
// lib/commands/test.js for the tests each one resolves to. Without both halves
|
|
48
|
+
// (targetable here + a resolved test there) a change falls to the full-suite lane.
|
|
49
|
+
'validation/',
|
|
50
|
+
'eval/',
|
|
51
|
+
'rules/',
|
|
52
|
+
'plugin/',
|
|
44
53
|
];
|
|
45
54
|
|
|
55
|
+
// Individually mapped root files (see DIRECT_TEST_CANDIDATES in lib/commands/test.js).
|
|
56
|
+
const KNOWN_TARGETABLE_FILES = new Set([
|
|
57
|
+
'lefthook.yml',
|
|
58
|
+
'eslint.config.js',
|
|
59
|
+
'.coderabbit.yaml',
|
|
60
|
+
'CODING_STANDARDS.md',
|
|
61
|
+
'.claude-plugin/marketplace.json',
|
|
62
|
+
'lib/kernel/schema.js',
|
|
63
|
+
'lib/kernel/migrations.js',
|
|
64
|
+
'lib/kernel/sqlite-driver.js',
|
|
65
|
+
'lib/commands/shepherd.js',
|
|
66
|
+
'lib/commands/github.js',
|
|
67
|
+
'lib/github-context.js',
|
|
68
|
+
'lib/commands/_registry.js',
|
|
69
|
+
'lib/commands/pr.js',
|
|
70
|
+
'lib/commands/ship.js',
|
|
71
|
+
'lib/commands/merge.js',
|
|
72
|
+
'lib/commands/team.js',
|
|
73
|
+
'lib/commands/clean.js',
|
|
74
|
+
'scripts/github-context-bridge.sh',
|
|
75
|
+
'lib/commands/push.js',
|
|
76
|
+
'lib/commands/hooks.js',
|
|
77
|
+
'lib/commands/serve.js',
|
|
78
|
+
'lib/commands/skill.js',
|
|
79
|
+
'scripts/lib/behavioral-eval-runtime.js',
|
|
80
|
+
'web/dashboard/generate-snapshot.mjs',
|
|
81
|
+
'lib/pr-monitor/watch-owner.js',
|
|
82
|
+
'lib/pr-monitor/reconcile.js',
|
|
83
|
+
'lib/pr-monitor/reconcile-executor.js',
|
|
84
|
+
'lib/pr-monitor/watch-lifecycle.js',
|
|
85
|
+
'lib/pr-monitor/shepherd-lease.js',
|
|
86
|
+
'lib/pr-monitor/monitor.js',
|
|
87
|
+
]);
|
|
88
|
+
|
|
46
89
|
const ALWAYS_RUN_RISK_TEST_TARGETS = [
|
|
47
90
|
// Windows + concurrent filesystem locking has failed post-merge; keep this
|
|
48
91
|
// in the fast PR lane until enough full-matrix runs prove it stable.
|
|
@@ -64,14 +107,29 @@ const isWindows = process.platform === 'win32';
|
|
|
64
107
|
// wedged). Raise it with FORGE_TEST_TIMEOUT_MS for slow machines.
|
|
65
108
|
const DEFAULT_TEST_COMMAND_TIMEOUT_MS = 5 * 60 * 1000;
|
|
66
109
|
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
const
|
|
110
|
+
// Measured wall-clock runtime of a HEALTHY, fully passing full suite on a
|
|
111
|
+
// developer Windows machine with pinned Bun 1.3.12 (2026-08-26):
|
|
112
|
+
//
|
|
113
|
+
// 8005 pass, 32 skip, 1 todo, 0 fail — 8038 tests across 594 files [602.43s]
|
|
114
|
+
//
|
|
115
|
+
// Any full-suite budget must clear this by a wide margin. Treat it as a floor to
|
|
116
|
+
// measure against, never as the budget itself.
|
|
117
|
+
const OBSERVED_FULL_SUITE_RUNTIME_MS = 602_430;
|
|
118
|
+
|
|
119
|
+
// Wall-clock budget for the FULL-SUITE lane (`scripts/test-full-suite.js` and the
|
|
120
|
+
// package-level `test` script run by `forge push`), used on package-level,
|
|
121
|
+
// unmapped, or zero-resolved changes.
|
|
122
|
+
//
|
|
123
|
+
// This ceiling exists to catch an INDEFINITE HANG (issue 8aef79e8: a synchronous
|
|
124
|
+
// git/bash spawn wedged during git mid-push state, observed ~50 min), not to
|
|
125
|
+
// bound normal runtime. So it is sized as ~2x the measured healthy runtime above,
|
|
126
|
+
// not fitted to it. The previous 10-min value was BELOW the 602.43s measurement,
|
|
127
|
+
// so it SIGKILLed passing suites — `forge push` died mid-suite with no summary
|
|
128
|
+
// and never pushed. 25 min is the next round number clearing 2x the measurement
|
|
129
|
+
// (1_204_860 ms). Do not shave this back toward the observed runtime; if a
|
|
130
|
+
// slower machine needs more, raise FORGE_TEST_TIMEOUT_MS (which still overrides)
|
|
131
|
+
// and re-measure before changing this default.
|
|
132
|
+
const DEFAULT_FULL_SUITE_TIMEOUT_MS = 25 * 60 * 1000;
|
|
75
133
|
|
|
76
134
|
// Conventional shell exit code for a command terminated by a timeout.
|
|
77
135
|
const TIMEOUT_EXIT_CODE = 124;
|
|
@@ -114,9 +172,10 @@ function resolveCommandTimeoutMs(env = process.env) {
|
|
|
114
172
|
}
|
|
115
173
|
|
|
116
174
|
/**
|
|
117
|
-
* Resolves the wall-clock budget for the full-suite
|
|
175
|
+
* Resolves the wall-clock budget for the full-suite lane. An explicit
|
|
118
176
|
* FORGE_TEST_TIMEOUT_MS override wins; otherwise it uses the larger,
|
|
119
|
-
*
|
|
177
|
+
* measurement-derived budget (~2x OBSERVED_FULL_SUITE_RUNTIME_MS) so a healthy
|
|
178
|
+
* full run is never failed fast.
|
|
120
179
|
*
|
|
121
180
|
* @param {NodeJS.ProcessEnv} [env=process.env] Environment to read the override from.
|
|
122
181
|
* @returns {number} Timeout in milliseconds (defaults to DEFAULT_FULL_SUITE_TIMEOUT_MS).
|
|
@@ -183,6 +242,10 @@ function isKnownTargetablePath(file) {
|
|
|
183
242
|
return true;
|
|
184
243
|
}
|
|
185
244
|
|
|
245
|
+
if (KNOWN_TARGETABLE_FILES.has(file)) {
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
|
|
186
249
|
if (file === 'README.md'
|
|
187
250
|
|| file === 'bin/forge.js'
|
|
188
251
|
|| file === 'bin/forge-cmd.js'
|
|
@@ -202,7 +265,11 @@ function isKnownTargetablePath(file) {
|
|
|
202
265
|
// SKILL_TEST_TARGETS in lib/commands/test.js); a skills-only PR stays on the
|
|
203
266
|
// targeted lane instead of the full suite.
|
|
204
267
|
|| file.startsWith('skills/')
|
|
205
|
-
|| file.startsWith('.agents/skills/')
|
|
268
|
+
|| file.startsWith('.agents/skills/')
|
|
269
|
+
// Maintainer-only contributor skills (tracked, never published). They map to
|
|
270
|
+
// the AGENTS.md docs-bleed gate in lib/commands/test.js, so a contributor-docs
|
|
271
|
+
// PR stays on the targeted lane instead of the full suite.
|
|
272
|
+
|| file.startsWith('.forge/contributor-skills/')) {
|
|
206
273
|
return true;
|
|
207
274
|
}
|
|
208
275
|
|
|
@@ -248,17 +315,28 @@ function buildTestExecutionPlan(projectRoot, execFileSync = defaultExecFileSync,
|
|
|
248
315
|
sinceUpstream: options.sinceUpstream !== false,
|
|
249
316
|
};
|
|
250
317
|
const changedFiles = getChangedFiles(execFileSync, diffOptions);
|
|
251
|
-
const affectedTestTargets = getAffectedTestFiles(projectRoot, execFileSync, fs,
|
|
318
|
+
const affectedTestTargets = getAffectedTestFiles(projectRoot, execFileSync, fs, {
|
|
319
|
+
...diffOptions,
|
|
320
|
+
changedFiles,
|
|
321
|
+
});
|
|
252
322
|
|
|
253
323
|
let runFullSuite = false;
|
|
254
324
|
let runTestEnv = false;
|
|
255
325
|
let runE2E = false;
|
|
256
326
|
let runWorkflowTests = includesWorkflowTarget(affectedTestTargets);
|
|
257
327
|
let hasUnmappedFiles = false;
|
|
328
|
+
let hasFullSuiteRequiredFiles = false;
|
|
329
|
+
let hasPackageLevelChanges = false;
|
|
258
330
|
const hasUnknownChangedFiles = changedFiles.length === 0 && affectedTestTargets.length === 0;
|
|
259
331
|
|
|
260
332
|
for (const file of changedFiles) {
|
|
333
|
+
if (isFullSuiteRequiredFile(file)) {
|
|
334
|
+
hasFullSuiteRequiredFiles = true;
|
|
335
|
+
runFullSuite = true;
|
|
336
|
+
}
|
|
337
|
+
|
|
261
338
|
if (PACKAGE_LEVEL_PATHS.has(file) || file.startsWith('packages/')) {
|
|
339
|
+
hasPackageLevelChanges = true;
|
|
262
340
|
runFullSuite = true;
|
|
263
341
|
runTestEnv = true;
|
|
264
342
|
runE2E = true;
|
|
@@ -309,8 +387,10 @@ function buildTestExecutionPlan(projectRoot, execFileSync = defaultExecFileSync,
|
|
|
309
387
|
? 'changed files could not be resolved safely'
|
|
310
388
|
: hasZeroResolvedTests
|
|
311
389
|
? 'known changes did not resolve runnable tests'
|
|
312
|
-
:
|
|
390
|
+
: hasPackageLevelChanges
|
|
313
391
|
? 'package-level changes detected'
|
|
392
|
+
: hasFullSuiteRequiredFiles
|
|
393
|
+
? 'shared authority changes require full unit coverage'
|
|
314
394
|
: 'known changes mapped to targeted tests';
|
|
315
395
|
|
|
316
396
|
return {
|
|
@@ -464,7 +544,7 @@ async function runTestExecutionPlan(plan, deps = {}) {
|
|
|
464
544
|
const label = deps.label || 'tests';
|
|
465
545
|
const timeout = resolveCommandTimeoutMs(env);
|
|
466
546
|
const laneOptions = { env: childEnv, killSignal: 'SIGKILL', timeout, processTree };
|
|
467
|
-
// The full-suite fallback gets
|
|
547
|
+
// The full-suite fallback gets the larger, measurement-derived budget so a
|
|
468
548
|
// healthy-but-slow full run is not failed fast by the targeted-lane ceiling.
|
|
469
549
|
const fullSuiteOptions = {
|
|
470
550
|
env: childEnv,
|
|
@@ -566,6 +646,7 @@ module.exports = {
|
|
|
566
646
|
ALWAYS_RUN_RISK_TEST_TARGETS,
|
|
567
647
|
DEFAULT_FULL_SUITE_TIMEOUT_MS,
|
|
568
648
|
DEFAULT_TEST_COMMAND_TIMEOUT_MS,
|
|
649
|
+
OBSERVED_FULL_SUITE_RUNTIME_MS,
|
|
569
650
|
QUICK_LANE_ENV_VAR,
|
|
570
651
|
QUICK_LANE_VALUE,
|
|
571
652
|
buildTestExecutionPlan,
|
package/skills/coverage.json
CHANGED
package/skills/review/SKILL.md
CHANGED
|
@@ -28,6 +28,8 @@ This skill handles ALL issues that arise after creating a pull request.
|
|
|
28
28
|
|
|
29
29
|
## What This Skill Does
|
|
30
30
|
|
|
31
|
+
Before reviewing a diff, read `CODING_STANDARDS.md` from the repository root and apply every relevant rule.
|
|
32
|
+
|
|
31
33
|
### Step 1: Fetch Complete PR Status
|
|
32
34
|
```bash
|
|
33
35
|
# Get full PR details including all checks
|
package/skills/setup/SKILL.md
CHANGED
|
@@ -30,6 +30,24 @@ forge setup --path <dir> --dry-run # preview against another d
|
|
|
30
30
|
|
|
31
31
|
`forge setup` is **interactive by default**. In any automated/agent context pass `--yes` (or `--non-interactive`) so it never blocks on a prompt.
|
|
32
32
|
|
|
33
|
+
# Optional GitHub accounts per clone
|
|
34
|
+
|
|
35
|
+
Only when the user opts into multiple accounts: sign in once per account with
|
|
36
|
+
native `gh auth login`, then select inside each intended clone:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
forge github use <login> # verify stored account/access before binding this clone
|
|
40
|
+
forge github status # safe identity and transport diagnostics; --json supported
|
|
41
|
+
forge github run -- <harness> # explicitly give a trusted child the selected GitHub identity
|
|
42
|
+
forge github unset # remove this binding, not stored logins
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Ordinary setup stays unbound. Never paste or persist tokens, automatically log in
|
|
46
|
+
or switch accounts, or change Git author/remotes/SSH/helper configuration.
|
|
47
|
+
Git transport is separate; HTTPS using `gh auth git-credential` can inherit the
|
|
48
|
+
explicit child session's account. See the repository reference guide
|
|
49
|
+
`docs/reference/github-accounts.md` for concurrent sessions and recovery.
|
|
50
|
+
|
|
33
51
|
# Initialize a fresh repo
|
|
34
52
|
|
|
35
53
|
```bash
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
"static": {
|
|
5
5
|
"token_cost": {
|
|
6
6
|
"desc_chars": 1021,
|
|
7
|
-
"body_lines":
|
|
8
|
-
"score":
|
|
7
|
+
"body_lines": 92,
|
|
8
|
+
"score": 41
|
|
9
9
|
},
|
|
10
10
|
"caps": {
|
|
11
11
|
"desc_within": true,
|
|
@@ -37,5 +37,5 @@
|
|
|
37
37
|
"variance": null,
|
|
38
38
|
"note": "behavioral — W5 (LLM judge): semantic recall/precision/chain/outcome/variance, not deterministic"
|
|
39
39
|
},
|
|
40
|
-
"composite":
|
|
40
|
+
"composite": 82
|
|
41
41
|
}
|
package/skills/shepherd/SKILL.md
CHANGED
|
@@ -27,7 +27,7 @@ Forge owns your open PRs. A singleton daemon converges every open PR toward merg
|
|
|
27
27
|
`shepherd` is the PR-ownership utility, not a workflow stage. It has two modes over the **single `forge shepherd` verb**:
|
|
28
28
|
|
|
29
29
|
- **Daemon (default ownership):** `forge shepherd daemon` is a machine-wide singleton for this repo. It heartbeats and converges the whole PR world every ~60s — self-registering hand-opened PRs, restarting killed watchers, reaping verified orphans, converging CI check state into kernel verdicts, retiring merged/closed PRs — then self-retires (releases the lease, kills its children, exits) once no PRs remain open. You do NOT poll by hand; the daemon does.
|
|
30
|
-
- **One-shot
|
|
30
|
+
- **One-shot convergence:** `forge shepherd <pr>` runs the local review preflight, reads one PR's current-head state, takes at most one idempotent Tier-A action, persists bounded deltas/receipts, and exits. Use it for a point-in-time question or one bounded convergence attempt on a single PR.
|
|
31
31
|
|
|
32
32
|
## Which mode? (decision rule)
|
|
33
33
|
|
|
@@ -57,12 +57,22 @@ forge shepherd events <pr> --since <seq> # only the new events since sequence
|
|
|
57
57
|
|
|
58
58
|
`--pull` is strictly read-only (dry-run pass: no rerun, no rebase, no merge, no thread resolution). It returns one bounded, actionable-only payload — `blockers[]`, classified `requiredChecks`, failed-check log `failures[]` (matrix-deduped), and every unresolved `reviewThreads[]` — so you get "everything blocking this PR + what to fix" in one call. Passing checks and satisfied policy are omitted.
|
|
59
59
|
|
|
60
|
+
A plain `forge shepherd <pr>` also returns a consolidated `localPreflight`, at
|
|
61
|
+
most 128 `deltas`, `receiptIds`, and one explicit `handoff`. The preflight probes
|
|
62
|
+
CodeRabbit and runs strict lint, Sonar parity, structural drift, and affected
|
|
63
|
+
tests. An unavailable or unauthenticated CodeRabbit CLI is reported as
|
|
64
|
+
`UNAVAILABLE`/`INCOMPLETE`, never as a fabricated pass; deterministic failures or
|
|
65
|
+
actual review findings make the remote decision read-only for that pass. A
|
|
66
|
+
checkout that is not the exact PR head is `NOT_APPLICABLE`, never reviewed as if
|
|
67
|
+
it were authoritative.
|
|
68
|
+
|
|
60
69
|
### Verdict vocabulary (collapsed, W-S1)
|
|
61
70
|
|
|
62
71
|
| Verdict | Meaning |
|
|
63
72
|
| --- | --- |
|
|
64
73
|
| `MERGE_READY` | Required checks green, branch up to date — hand off to a human to merge. |
|
|
65
74
|
| `PENDING` | A Tier-A action was taken, or checks are still running — await the next tick/pass. |
|
|
75
|
+
| `INCOMPLETE` | Exact-head local review or durable convergence evidence is unavailable — fail closed and retry after evidence is restored. |
|
|
66
76
|
| `BLOCKED` | Something actionable blocks merge (failing/missing/skipped required check, conflict, behind, unresolved threads, changes requested). Read `blockers[]`. |
|
|
67
77
|
| `CI_DEAD_HEAD` | The head has no required checks running (e.g. an auto-update authored by `GITHUB_TOKEN` never re-triggered CI). Recovery is an **escalation, not an autonomous Tier-A rerun**: it needs a maintainer-provided `FORGE_PR_TOKEN` (contents + pull-requests + checks) to re-author the push so CI re-triggers. |
|
|
68
78
|
| `ESCALATE` | A Tier-C condition (conflict, unreadable required set, persistent failure, oscillation, budget exhaustion). Context is posted to the PR. |
|
|
@@ -109,4 +119,11 @@ before lease, Kernel-state, or process work. All leave the manual
|
|
|
109
119
|
|
|
110
120
|
## State
|
|
111
121
|
|
|
112
|
-
|
|
122
|
+
Public Memory is the durable monitor authority and public Flow reducers restore
|
|
123
|
+
its bounded event and watcher-process checkpoints after restart. The per-PR
|
|
124
|
+
journal under `.forge/pr-monitor/<repo>-<pr>/` remains a compatibility delivery
|
|
125
|
+
surface for `events --since`, not authority. Merged/closed evidence produces one
|
|
126
|
+
idempotent terminal MonitorReceipt; conflicting or incomplete replay fails
|
|
127
|
+
closed. For 0.1 no receipt grants continuing lease authority: live ownership is
|
|
128
|
+
re-probed at consequential boundaries, and canonical LeaseReceipt epoch/scope
|
|
129
|
+
plus the same-actor/session ABA fix remain explicitly deferred.
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
"static": {
|
|
5
5
|
"token_cost": {
|
|
6
6
|
"desc_chars": 987,
|
|
7
|
-
"body_lines":
|
|
8
|
-
"score":
|
|
7
|
+
"body_lines": 109,
|
|
8
|
+
"score": 41
|
|
9
9
|
},
|
|
10
10
|
"caps": {
|
|
11
11
|
"desc_within": true,
|
|
@@ -37,5 +37,5 @@
|
|
|
37
37
|
"variance": null,
|
|
38
38
|
"note": "behavioral — W5 (LLM judge): semantic recall/precision/chain/outcome/variance, not deterministic"
|
|
39
39
|
},
|
|
40
|
-
"composite":
|
|
40
|
+
"composite": 82
|
|
41
41
|
}
|
package/skills/validate/SKILL.md
CHANGED
|
@@ -105,6 +105,9 @@ bun run lint # or: npm run lint, eslint ., etc.
|
|
|
105
105
|
- Best practices compliance
|
|
106
106
|
|
|
107
107
|
### Step 3: Code Review (if available)
|
|
108
|
+
|
|
109
|
+
Read `CODING_STANDARDS.md` from the repository root before invoking the reviewer so the review applies the repository's diff-level contract.
|
|
110
|
+
|
|
108
111
|
```bash
|
|
109
112
|
/code-review:code-review
|
|
110
113
|
```
|