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.
- package/AGENTS.md +14 -7
- package/CHANGELOG.md +43 -1
- package/README.md +6 -2
- package/bin/forge-cmd.js +21 -1
- package/bin/forge.js +16 -369
- 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 +9 -4
- 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 +117 -17
- package/lefthook.yml +12 -0
- package/lib/activation/ensure-forge-home.js +33 -15
- package/lib/adapters/greptile-review-adapter.js +1 -1
- package/lib/adapters/pr-state-adapter.js +397 -100
- package/lib/agents-config.js +5 -0
- package/lib/audit-evidence.js +71 -110
- package/lib/capped-jsonl-log.js +236 -0
- package/lib/commands/_issue.js +31 -46
- package/lib/commands/_manifest.js +1 -1
- package/lib/commands/_registry.js +2 -2
- package/lib/commands/_resolve-command-opts.js +36 -29
- package/lib/commands/claim.js +2 -4
- package/lib/commands/clean.js +196 -32
- package/lib/commands/dev.js +4 -33
- package/lib/commands/hooks.js +358 -13
- package/lib/commands/insights.js +8 -3
- package/lib/commands/merge.js +600 -40
- package/lib/commands/plan.js +23 -115
- package/lib/commands/pr.js +1 -1
- package/lib/commands/preflight.js +11 -2
- package/lib/commands/prime.js +23 -3
- 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 +18 -4
- package/lib/commands/serve.js +5 -2
- package/lib/commands/setup.js +191 -95
- package/lib/commands/shepherd.js +49 -4
- package/lib/commands/ship.js +22 -23
- package/lib/commands/skill.js +383 -0
- package/lib/commands/status.js +54 -33
- package/lib/commands/test.js +56 -34
- package/lib/commands/worktree.js +247 -43
- package/lib/core/runtime-graph.js +89 -15
- 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 +64 -491
- package/lib/git-defaults.js +56 -0
- package/lib/harness-capability-matrix.js +5 -5
- package/lib/hook-renderer.js +147 -16
- package/lib/insights.js +96 -80
- package/lib/issue-backend.js +42 -3
- package/lib/kernel/backing-issue.js +14 -2
- package/lib/kernel/broker.js +44 -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/migrations.js +30 -2
- package/lib/kernel/schema.js +35 -0
- package/lib/kernel/sqlite-driver.js +292 -18
- 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 +212 -0
- package/lib/merge-rules.js +8 -4
- package/lib/npm-publish-workflow.js +272 -0
- package/lib/orientation.js +371 -49
- package/lib/plugin-catalog.js +14 -4
- package/lib/pr-bundle.js +9 -6
- package/lib/pr-monitor/journal.js +18 -2
- package/lib/pr-monitor/reconcile-executor.js +842 -0
- package/lib/pr-monitor/reconcile-tick.js +138 -0
- package/lib/pr-monitor/reconcile.js +0 -0
- package/lib/pr-monitor/render-summary.js +196 -0
- package/lib/pr-monitor/shepherd-lease.js +252 -0
- package/lib/pr-monitor/watch-lifecycle.js +14 -2
- package/lib/pr-pull.js +98 -24
- 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 +40 -0
- 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/rules-sync.js +4 -0
- package/lib/runtime-health.js +15 -46
- package/lib/shell-utils.js +1 -1
- package/lib/skill-eval.js +750 -0
- package/lib/skills-sync.js +6 -3
- package/lib/smart-merge.js +28 -4
- package/lib/status/identity.js +46 -0
- package/lib/status/presenter.js +0 -35
- package/lib/status/snapshot.js +11 -16
- package/lib/symlink-utils.js +74 -26
- package/lib/upgrade-safety.js +47 -9
- package/lib/using-forge.js +328 -0
- package/lib/workflow/enforce-stage.js +5 -5
- package/lib/workflow/state-manager.js +23 -23
- package/package.json +6 -7
- package/rules/using-forge.md +24 -0
- package/scripts/doc-asserting-tests.js +158 -0
- package/scripts/forge-team/index.sh +0 -5
- package/scripts/forge-team/tests/dispatcher.test.sh +1 -1
- package/scripts/forge-team/tests/workflow-integration.test.sh +0 -1
- 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 +184 -44
- package/skills/claim-safety/SKILL.md +4 -0
- package/skills/claim-safety/evals/scorecard.json +41 -0
- package/skills/coverage.json +83 -0
- package/skills/dev/SKILL.md +4 -0
- package/skills/dev/evals/scorecard.json +41 -0
- package/skills/gates/SKILL.md +80 -0
- package/skills/gates/evals/evals.json +38 -0
- package/skills/gates/evals/scorecard.json +41 -0
- package/skills/hermes-forge/SKILL.md +1 -0
- package/skills/hermes-forge/evals/scorecard.json +41 -0
- package/skills/issue-basics/SKILL.md +1 -0
- package/skills/issue-basics/evals/scorecard.json +41 -0
- package/skills/kernel/SKILL.md +38 -0
- package/skills/kernel/evals/scorecard.json +41 -0
- package/skills/memory/SKILL.md +16 -1
- package/skills/memory/evals/scorecard.json +41 -0
- package/skills/parallel-deep-research/SKILL.md +1 -0
- package/skills/parallel-deep-research/evals/scorecard.json +41 -0
- package/skills/plan/SKILL.md +6 -0
- package/skills/plan/evals/scorecard.json +41 -0
- package/skills/portability/SKILL.md +47 -0
- package/skills/portability/evals/evals.json +34 -0
- package/skills/portability/evals/scorecard.json +41 -0
- package/skills/research/SKILL.md +1 -0
- package/skills/research/evals/scorecard.json +41 -0
- package/skills/review/SKILL.md +10 -11
- package/skills/review/evals/scorecard.json +41 -0
- package/skills/rollback/SKILL.md +5 -11
- package/skills/rollback/evals/scorecard.json +41 -0
- package/skills/setup/SKILL.md +91 -0
- package/skills/setup/evals/evals.json +42 -0
- package/skills/setup/evals/scorecard.json +41 -0
- package/skills/shepherd/SKILL.md +84 -38
- package/skills/shepherd/evals/evals.json +21 -9
- package/skills/shepherd/evals/scorecard.json +41 -0
- package/skills/ship/SKILL.md +10 -12
- package/skills/ship/evals/scorecard.json +41 -0
- package/skills/smith/SKILL.md +8 -0
- package/skills/smith/evals/scorecard.json +41 -0
- package/skills/sonarcloud/SKILL.md +1 -0
- package/skills/sonarcloud/evals/scorecard.json +41 -0
- package/skills/sonarcloud-analysis/SKILL.md +1 -0
- package/skills/sonarcloud-analysis/evals/scorecard.json +41 -0
- package/skills/status/SKILL.md +3 -0
- package/skills/status/evals/scorecard.json +41 -0
- package/skills/triage-ready/SKILL.md +2 -0
- package/skills/triage-ready/evals/scorecard.json +41 -0
- package/skills/using-forge/SKILL.md +104 -0
- package/skills/using-forge/evals/scorecard.json +41 -0
- package/skills/validate/SKILL.md +4 -0
- package/skills/validate/evals/scorecard.json +41 -0
- package/skills/verify/SKILL.md +4 -0
- package/skills/verify/evals/scorecard.json +41 -0
- package/skills/worktree/SKILL.md +92 -0
- package/skills/worktree/evals/evals.json +38 -0
- package/skills/worktree/evals/scorecard.json +41 -0
- package/lib/adapters/beads-issue-adapter.js +0 -127
- package/lib/beads-nudge.js +0 -91
- package/lib/beads-setup.js +0 -538
- package/lib/beads-sync-scaffold.js +0 -189
- package/lib/commands/board.js +0 -64
- package/lib/pat-setup.js +0 -207
- package/lib/pr-monitor/render-sticky.js +0 -192
- package/lib/pr-monitor/upsert-sticky.js +0 -169
- package/lib/status/beads-snapshot.js +0 -145
- 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/forge-team/lib/dashboard.sh +0 -316
- package/scripts/forge-team/tests/dashboard.test.sh +0 -155
- package/scripts/lib/beads-migrate-to-dolt.mjs +0 -503
package/scripts/test.js
CHANGED
|
@@ -4,15 +4,27 @@
|
|
|
4
4
|
*
|
|
5
5
|
* It runs only the tests affected by known changes when possible and falls back
|
|
6
6
|
* to the full suite for package-level or unknown-file changes.
|
|
7
|
+
*
|
|
8
|
+
* Quick lane: `forge push --quick` sets FORGE_PUSH_LANE=quick on the `git push`
|
|
9
|
+
* it spawns, declaring the lint-only review-cycle lane. The pre-push entry point
|
|
10
|
+
* honors that declaration and skips the test run with a loud notice, so --quick
|
|
11
|
+
* is quick end to end instead of only in the step push.js controls. This is not
|
|
12
|
+
* a hook bypass: branch protection and lint still run, local validation
|
|
13
|
+
* (`--validate`) ignores the variable entirely, and CI runs the full matrix.
|
|
7
14
|
*/
|
|
8
15
|
|
|
9
|
-
const {
|
|
16
|
+
const { EventEmitter } = require('node:events');
|
|
17
|
+
const {
|
|
18
|
+
execFileSync: defaultExecFileSync,
|
|
19
|
+
spawn: defaultSpawn,
|
|
20
|
+
} = require('node:child_process');
|
|
10
21
|
const fs = require('node:fs');
|
|
11
22
|
|
|
12
23
|
const {
|
|
13
24
|
getAffectedTestFiles,
|
|
14
25
|
getChangedFiles,
|
|
15
26
|
} = require('../lib/commands/test');
|
|
27
|
+
const { createProcessTree, signalExitCode } = require('./process-tree');
|
|
16
28
|
|
|
17
29
|
const PACKAGE_LEVEL_PATHS = new Set([
|
|
18
30
|
'package.json',
|
|
@@ -64,6 +76,21 @@ const DEFAULT_FULL_SUITE_TIMEOUT_MS = 10 * 60 * 1000;
|
|
|
64
76
|
// Conventional shell exit code for a command terminated by a timeout.
|
|
65
77
|
const TIMEOUT_EXIT_CODE = 124;
|
|
66
78
|
|
|
79
|
+
// Lane declaration set by `forge push --quick` on the spawned `git push`.
|
|
80
|
+
// Only this exact value opts into the lint-only lane.
|
|
81
|
+
const QUICK_LANE_ENV_VAR = 'FORGE_PUSH_LANE';
|
|
82
|
+
const QUICK_LANE_VALUE = 'quick';
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Reports whether the environment declares the lint-only quick push lane.
|
|
86
|
+
*
|
|
87
|
+
* @param {NodeJS.ProcessEnv} [env=process.env] Environment to inspect.
|
|
88
|
+
* @returns {boolean} True when the quick lane is explicitly declared.
|
|
89
|
+
*/
|
|
90
|
+
function isQuickPushLane(env = process.env) {
|
|
91
|
+
return env[QUICK_LANE_ENV_VAR] === QUICK_LANE_VALUE;
|
|
92
|
+
}
|
|
93
|
+
|
|
67
94
|
/**
|
|
68
95
|
* Reads and validates the FORGE_TEST_TIMEOUT_MS override, if any.
|
|
69
96
|
*
|
|
@@ -134,9 +161,11 @@ function detectPackageManager() {
|
|
|
134
161
|
function stripGitHookEnv(sourceEnv = process.env) {
|
|
135
162
|
const env = { ...sourceEnv };
|
|
136
163
|
for (const key of Object.keys(env)) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
||
|
|
164
|
+
const normalizedKey = key.toUpperCase();
|
|
165
|
+
if (normalizedKey === 'GIT_DIR' || normalizedKey === 'GIT_WORK_TREE'
|
|
166
|
+
|| normalizedKey === 'GIT_INDEX_FILE' || normalizedKey === 'GIT_OBJECT_DIRECTORY'
|
|
167
|
+
|| normalizedKey === 'GIT_ALTERNATE_OBJECT_DIRECTORIES'
|
|
168
|
+
|| normalizedKey === 'GIT_QUARANTINE_PATH') {
|
|
140
169
|
delete env[key];
|
|
141
170
|
}
|
|
142
171
|
}
|
|
@@ -156,6 +185,8 @@ function isKnownTargetablePath(file) {
|
|
|
156
185
|
|
|
157
186
|
if (file === 'README.md'
|
|
158
187
|
|| file === 'bin/forge.js'
|
|
188
|
+
|| file === 'bin/forge-cmd.js'
|
|
189
|
+
|| file === 'bin/forge-preflight.js'
|
|
159
190
|
|| file === 'QUICKSTART.md'
|
|
160
191
|
|| file === 'CHANGELOG.md'
|
|
161
192
|
|| file === 'AGENTS.md'
|
|
@@ -166,7 +197,12 @@ function isKnownTargetablePath(file) {
|
|
|
166
197
|
|| file.startsWith('docs/guides/')
|
|
167
198
|
|| file.startsWith('docs/plans/')
|
|
168
199
|
|| file.startsWith('docs/reference/')
|
|
169
|
-
|| file.startsWith('docs/work/')
|
|
200
|
+
|| file.startsWith('docs/work/')
|
|
201
|
+
// Skill sources + their committed mirror map to the skill suite (see
|
|
202
|
+
// SKILL_TEST_TARGETS in lib/commands/test.js); a skills-only PR stays on the
|
|
203
|
+
// targeted lane instead of the full suite.
|
|
204
|
+
|| file.startsWith('skills/')
|
|
205
|
+
|| file.startsWith('.agents/skills/')) {
|
|
170
206
|
return true;
|
|
171
207
|
}
|
|
172
208
|
|
|
@@ -310,31 +346,78 @@ function classifyPushTests(projectRoot, execFileSync = defaultExecFileSync) {
|
|
|
310
346
|
*
|
|
311
347
|
* @param {string} command Executable name.
|
|
312
348
|
* @param {string[]} args Command arguments.
|
|
313
|
-
* @param {import('node:child_process').
|
|
314
|
-
* @param {typeof
|
|
315
|
-
* @returns {number} Process exit status, or 1 when no status is reported.
|
|
349
|
+
* @param {import('node:child_process').SpawnOptions} [options={}] Spawn options.
|
|
350
|
+
* @param {typeof defaultSpawn} [spawn=defaultSpawn] Process runner.
|
|
351
|
+
* @returns {Promise<number>} Process exit status, or 1 when no status is reported.
|
|
316
352
|
*/
|
|
317
|
-
function runCommand(command, args, options = {},
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
return
|
|
353
|
+
function runCommand(command, args, options = {}, spawn = defaultSpawn) {
|
|
354
|
+
const { processTree, timeout, killSignal: _killSignal, ...spawnOptions } = options;
|
|
355
|
+
return new Promise((resolve, reject) => {
|
|
356
|
+
let child;
|
|
357
|
+
let settled = false;
|
|
358
|
+
let timer = null;
|
|
359
|
+
const reservation = processTree?.reserveChild?.({ kind: 'test-lane', label: command }) || null;
|
|
360
|
+
let registered = false;
|
|
361
|
+
const release = () => {
|
|
362
|
+
if (registered) {
|
|
363
|
+
processTree?.unregisterChild?.(reservation);
|
|
364
|
+
registered = false;
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
const finish = (status) => {
|
|
368
|
+
if (settled) return;
|
|
369
|
+
settled = true;
|
|
370
|
+
release();
|
|
371
|
+
resolve(status ?? 1);
|
|
372
|
+
};
|
|
373
|
+
const fail = (error) => {
|
|
374
|
+
if (settled) return;
|
|
375
|
+
if (timer) clearTimeout(timer);
|
|
376
|
+
if (error?.code === 'ETIMEDOUT') {
|
|
377
|
+
processTree?.cleanup?.('SIGKILL');
|
|
378
|
+
console.error('');
|
|
379
|
+
console.error('Test lane exceeded its wall-clock ceiling and was terminated.');
|
|
380
|
+
console.error('A single test likely hung (e.g. a spawned git/bash call that never returns).');
|
|
381
|
+
console.error('Adjust the ceiling with FORGE_TEST_TIMEOUT_MS. Failing the run instead of');
|
|
382
|
+
console.error('blocking the push. See issue 8aef79e8.');
|
|
383
|
+
console.error('');
|
|
384
|
+
finish(TIMEOUT_EXIT_CODE);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
settled = true;
|
|
388
|
+
release();
|
|
389
|
+
reject(error);
|
|
390
|
+
};
|
|
391
|
+
try {
|
|
392
|
+
child = spawn(command, args, { stdio: 'inherit', shell: isWindows, ...spawnOptions });
|
|
393
|
+
} catch (error) {
|
|
394
|
+
fail(error);
|
|
395
|
+
return;
|
|
333
396
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
397
|
+
child.once?.('error', fail);
|
|
398
|
+
child.once?.('close', (status, signal) => {
|
|
399
|
+
if (timer) clearTimeout(timer);
|
|
400
|
+
finish(status ?? signalExitCode(signal));
|
|
401
|
+
});
|
|
402
|
+
if (reservation && typeof processTree?.registerChild === 'function') {
|
|
403
|
+
registered = true;
|
|
404
|
+
if (!processTree.registerChild(reservation, child)) {
|
|
405
|
+
processTree.abortChild?.(reservation, child);
|
|
406
|
+
release();
|
|
407
|
+
fail(new Error('test lane process could not be registered'));
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
timer = timeout > 0 ? setTimeout(() => {
|
|
412
|
+
processTree?.cleanup?.('SIGKILL');
|
|
413
|
+
try { child.kill?.('SIGKILL'); } catch { /* best effort */ }
|
|
414
|
+
finish(TIMEOUT_EXIT_CODE);
|
|
415
|
+
}, timeout) : null;
|
|
416
|
+
if (!child.once) {
|
|
417
|
+
if (timer) clearTimeout(timer);
|
|
418
|
+
finish(child.status ?? 1);
|
|
419
|
+
}
|
|
420
|
+
});
|
|
338
421
|
}
|
|
339
422
|
|
|
340
423
|
/**
|
|
@@ -344,51 +427,92 @@ function runCommand(command, args, options = {}, spawnSync = defaultSpawnSync) {
|
|
|
344
427
|
* @param {Object} [deps={}] Runtime dependencies for tests.
|
|
345
428
|
* @returns {number} Exit status for the executed plan.
|
|
346
429
|
*/
|
|
347
|
-
function runTestExecutionPlan(plan, deps = {}) {
|
|
348
|
-
const
|
|
430
|
+
async function runTestExecutionPlan(plan, deps = {}) {
|
|
431
|
+
const usesSpawnSyncAdapter = !deps.spawn && typeof deps.spawnSync === 'function';
|
|
432
|
+
const spawn = deps.spawn || (usesSpawnSyncAdapter ? (...args) => {
|
|
433
|
+
const child = new EventEmitter();
|
|
434
|
+
let result;
|
|
435
|
+
try {
|
|
436
|
+
result = deps.spawnSync(...args);
|
|
437
|
+
} catch (error) {
|
|
438
|
+
process.nextTick(() => child.emit('error', error));
|
|
439
|
+
return child;
|
|
440
|
+
}
|
|
441
|
+
child.pid = result?.pid || process.pid;
|
|
442
|
+
process.nextTick(() => {
|
|
443
|
+
if (result?.error) child.emit('error', result.error);
|
|
444
|
+
else child.emit('close', result?.status ?? 1, result?.signal);
|
|
445
|
+
});
|
|
446
|
+
return child;
|
|
447
|
+
} : defaultSpawn);
|
|
349
448
|
const pkgManager = deps.pkgManager || detectPackageManager();
|
|
350
449
|
const env = deps.env || stripGitHookEnv(process.env);
|
|
450
|
+
// The injected synchronous runner has already exited when its adapter returns,
|
|
451
|
+
// so there is no live child to register or reap through host process probes.
|
|
452
|
+
const processTree = deps.processTree || (usesSpawnSyncAdapter ? {} : createProcessTree({
|
|
453
|
+
env,
|
|
454
|
+
platform: deps.platform,
|
|
455
|
+
}));
|
|
456
|
+
let signal = null;
|
|
457
|
+
const removeSignalHandlers = typeof processTree.installSignalHandlers === 'function'
|
|
458
|
+
? processTree.installSignalHandlers((received) => {
|
|
459
|
+
signal = received;
|
|
460
|
+
})
|
|
461
|
+
: () => {};
|
|
462
|
+
const childEnv = typeof processTree.envFor === 'function' ? processTree.envFor(env) : env;
|
|
351
463
|
const bunCommand = deps.bunCommand || env.BUN_EXE || process.env.BUN_EXE || 'bun';
|
|
352
464
|
const label = deps.label || 'tests';
|
|
353
465
|
const timeout = resolveCommandTimeoutMs(env);
|
|
354
|
-
const laneOptions = { env, killSignal: 'SIGKILL', timeout };
|
|
466
|
+
const laneOptions = { env: childEnv, killSignal: 'SIGKILL', timeout, processTree };
|
|
355
467
|
// The full-suite fallback gets a larger, validation-aligned budget so a
|
|
356
468
|
// healthy-but-slow full run is not failed fast by the targeted-lane ceiling.
|
|
357
|
-
const fullSuiteOptions = {
|
|
469
|
+
const fullSuiteOptions = {
|
|
470
|
+
env: childEnv,
|
|
471
|
+
killSignal: 'SIGKILL',
|
|
472
|
+
timeout: resolveFullSuiteTimeoutMs(env),
|
|
473
|
+
processTree,
|
|
474
|
+
};
|
|
358
475
|
|
|
359
476
|
console.log(`Running ${label} (${pkgManager})...`);
|
|
360
477
|
|
|
361
478
|
try {
|
|
362
479
|
if (plan.runFullSuite) {
|
|
363
480
|
console.log(` Mode: full suite (${plan.reason})`);
|
|
364
|
-
|
|
481
|
+
const status = await runCommand('node', ['scripts/test-full-suite.js'], fullSuiteOptions, spawn);
|
|
482
|
+
if (signal) return signalExitCode(signal);
|
|
365
483
|
if (status !== 0) return status;
|
|
366
484
|
} else if (plan.testTargets.length > 0) {
|
|
367
485
|
console.log(` Mode: targeted (${plan.testTargets.length} test file${plan.testTargets.length === 1 ? '' : 's'})`);
|
|
368
486
|
const command = pkgManager === 'bun' ? bunCommand : pkgManager;
|
|
369
|
-
|
|
487
|
+
const status = await runCommand(command, ['run', 'test', ...plan.testTargets], laneOptions, spawn);
|
|
488
|
+
if (signal) return signalExitCode(signal);
|
|
370
489
|
if (status !== 0) return status;
|
|
371
490
|
}
|
|
372
491
|
|
|
373
492
|
if (plan.runE2E) {
|
|
374
493
|
console.log(' Extra: running affected e2e tests');
|
|
375
|
-
|
|
494
|
+
const status = await runCommand(bunCommand, ['test', '--timeout', '15000', 'test/e2e/'], laneOptions, spawn);
|
|
495
|
+
if (signal) return signalExitCode(signal);
|
|
376
496
|
if (status !== 0) return status;
|
|
377
497
|
}
|
|
378
498
|
|
|
379
499
|
if (!plan.runFullSuite && plan.runTestEnv) {
|
|
380
500
|
console.log(' Extra: running affected edge-case tests');
|
|
381
|
-
|
|
501
|
+
const status = await runCommand(bunCommand, ['test', '--timeout', '15000', 'test-env/'], laneOptions, spawn);
|
|
502
|
+
if (signal) return signalExitCode(signal);
|
|
382
503
|
if (status !== 0) return status;
|
|
383
504
|
}
|
|
384
505
|
|
|
385
506
|
console.log('Relevant tests passed');
|
|
386
|
-
return 0;
|
|
507
|
+
return signal ? signalExitCode(signal) : 0;
|
|
387
508
|
} catch (error) {
|
|
388
509
|
console.error('');
|
|
389
510
|
console.error(`Failed to run ${label}: ${error.message}`);
|
|
390
511
|
console.error('');
|
|
391
|
-
return 1;
|
|
512
|
+
return signal ? signalExitCode(signal) : 1;
|
|
513
|
+
} finally {
|
|
514
|
+
removeSignalHandlers();
|
|
515
|
+
processTree.cleanup?.(signal ? 'SIGKILL' : 'SIGTERM');
|
|
392
516
|
}
|
|
393
517
|
}
|
|
394
518
|
|
|
@@ -399,7 +523,15 @@ function runTestExecutionPlan(plan, deps = {}) {
|
|
|
399
523
|
* @param {Object} [deps={}] Runtime dependencies for tests.
|
|
400
524
|
* @returns {number} Exit status for pre-push tests.
|
|
401
525
|
*/
|
|
402
|
-
function runPrePushTests(projectRoot = process.cwd(), deps = {}) {
|
|
526
|
+
async function runPrePushTests(projectRoot = process.cwd(), deps = {}) {
|
|
527
|
+
if (isQuickPushLane(deps.env || process.env)) {
|
|
528
|
+
console.log('');
|
|
529
|
+
console.log(' quick lane: tests skipped locally — CI runs the full matrix');
|
|
530
|
+
console.log(' Run `forge push` (no --quick) to test before merge.');
|
|
531
|
+
console.log('');
|
|
532
|
+
return 0;
|
|
533
|
+
}
|
|
534
|
+
|
|
403
535
|
const execFileSync = deps.execFileSync || defaultExecFileSync;
|
|
404
536
|
const plan = classifyPushTests(projectRoot, execFileSync);
|
|
405
537
|
return runTestExecutionPlan(plan, { ...deps, label: 'pre-push tests' });
|
|
@@ -412,26 +544,34 @@ function runPrePushTests(projectRoot = process.cwd(), deps = {}) {
|
|
|
412
544
|
* @param {Object} [deps={}] Runtime dependencies for tests.
|
|
413
545
|
* @returns {number} Exit status for local validation tests.
|
|
414
546
|
*/
|
|
415
|
-
function runLocalValidationTests(projectRoot = process.cwd(), deps = {}) {
|
|
547
|
+
async function runLocalValidationTests(projectRoot = process.cwd(), deps = {}) {
|
|
416
548
|
const execFileSync = deps.execFileSync || defaultExecFileSync;
|
|
417
549
|
const plan = buildTestExecutionPlan(projectRoot, execFileSync, { sinceUpstream: true });
|
|
418
550
|
return runTestExecutionPlan(plan, { ...deps, label: 'local validation tests' });
|
|
419
551
|
}
|
|
420
552
|
|
|
421
553
|
if (require.main === module) {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
554
|
+
(async () => {
|
|
555
|
+
const exitCode = process.argv.includes('--validate')
|
|
556
|
+
? await runLocalValidationTests()
|
|
557
|
+
: await runPrePushTests();
|
|
558
|
+
process.exit(exitCode);
|
|
559
|
+
})().catch((error) => {
|
|
560
|
+
console.error(error);
|
|
561
|
+
process.exit(1);
|
|
562
|
+
});
|
|
426
563
|
}
|
|
427
564
|
|
|
428
565
|
module.exports = {
|
|
429
566
|
ALWAYS_RUN_RISK_TEST_TARGETS,
|
|
430
567
|
DEFAULT_FULL_SUITE_TIMEOUT_MS,
|
|
431
568
|
DEFAULT_TEST_COMMAND_TIMEOUT_MS,
|
|
569
|
+
QUICK_LANE_ENV_VAR,
|
|
570
|
+
QUICK_LANE_VALUE,
|
|
432
571
|
buildTestExecutionPlan,
|
|
433
572
|
classifyPushTests,
|
|
434
573
|
detectPackageManager,
|
|
574
|
+
isQuickPushLane,
|
|
435
575
|
resolveCommandTimeoutMs,
|
|
436
576
|
resolveFullSuiteTimeoutMs,
|
|
437
577
|
runLocalValidationTests,
|
|
@@ -14,10 +14,14 @@ description: >
|
|
|
14
14
|
and NOT for read-only selecting or ranking the next ready issue without claiming (that is
|
|
15
15
|
triage-ready).
|
|
16
16
|
allowed-tools: Read, Bash(forge:*)
|
|
17
|
+
next: dev
|
|
18
|
+
terminal: false
|
|
17
19
|
---
|
|
18
20
|
|
|
19
21
|
# Claim safety — claim, then prove you own the lease
|
|
20
22
|
|
|
23
|
+
> **Chain:** once you have PROVEN the lease (`forge issue owns <id>` exits 0), continue to `dev` to work the issue (`smith` proceeds into plan/dev after this proof). Usable standalone whenever ownership is in question.
|
|
24
|
+
|
|
21
25
|
Claiming is not owning. The Forge kernel keys a claim's idempotency on
|
|
22
26
|
`claim.create:<issue_id>:<actor>`, so a **same-key duplicate replay returns
|
|
23
27
|
`ok:true`** echoing the *current* call's `claim_id`. A genuine cross-actor
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill": "claim-safety",
|
|
3
|
+
"fixtures": "present",
|
|
4
|
+
"static": {
|
|
5
|
+
"token_cost": {
|
|
6
|
+
"desc_chars": 995,
|
|
7
|
+
"body_lines": 88,
|
|
8
|
+
"score": 43
|
|
9
|
+
},
|
|
10
|
+
"caps": {
|
|
11
|
+
"desc_within": true,
|
|
12
|
+
"body_within": true,
|
|
13
|
+
"score": 100
|
|
14
|
+
},
|
|
15
|
+
"description_quality": {
|
|
16
|
+
"has_trigger_cues": true,
|
|
17
|
+
"has_disambiguation_cues": true,
|
|
18
|
+
"adequate_length": true,
|
|
19
|
+
"score": 100
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"router_reachability": {
|
|
23
|
+
"has_curated_rule": true,
|
|
24
|
+
"router_exempt": false,
|
|
25
|
+
"fixtures": "present",
|
|
26
|
+
"fixtures_total": 6,
|
|
27
|
+
"fixtures_best_hit": 2,
|
|
28
|
+
"reachable": true,
|
|
29
|
+
"keyword_alignment": 0.33
|
|
30
|
+
},
|
|
31
|
+
"behavioral": {
|
|
32
|
+
"trigger_recall": null,
|
|
33
|
+
"trigger_precision": null,
|
|
34
|
+
"disambiguation": null,
|
|
35
|
+
"chain_correctness": null,
|
|
36
|
+
"outcome_quality": null,
|
|
37
|
+
"variance": null,
|
|
38
|
+
"note": "behavioral — W5 (LLM judge): semantic recall/precision/chain/outcome/variance, not deterministic"
|
|
39
|
+
},
|
|
40
|
+
"composite": 83
|
|
41
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"description": "Command -> owning-skill coverage map (plan 2026-07-20-next-phase-plan.md §3.3). Every registered user-facing command MUST map to an existing skill OR carry an explicit { exempt: <reason> }. Enforced by the coverage gate (lib/skill-eval.js evaluateCoverage, surfaced by `forge skill coverage`/`forge skill scores`, gated in test/skill-eval.test.js). Exempts reference the pending skill from plan §3.2 and are tightened as those skills land.",
|
|
4
|
+
"commands": {
|
|
5
|
+
"plan": "plan",
|
|
6
|
+
"dev": "dev",
|
|
7
|
+
"validate": "validate",
|
|
8
|
+
"ship": "ship",
|
|
9
|
+
"shepherd": "shepherd",
|
|
10
|
+
"status": "status",
|
|
11
|
+
"prime": "status",
|
|
12
|
+
"orient": "hermes-forge",
|
|
13
|
+
"recap": "hermes-forge",
|
|
14
|
+
"remember": "memory",
|
|
15
|
+
"recall": "memory",
|
|
16
|
+
"memory": "memory",
|
|
17
|
+
"ready": "triage-ready",
|
|
18
|
+
"blocked": "triage-ready",
|
|
19
|
+
"claim": "claim-safety",
|
|
20
|
+
"claims": "claim-safety",
|
|
21
|
+
"issue": "issue-basics",
|
|
22
|
+
"issues": "issue-basics",
|
|
23
|
+
"create": "issue-basics",
|
|
24
|
+
"close": "issue-basics",
|
|
25
|
+
"comment": "issue-basics",
|
|
26
|
+
"show": "issue-basics",
|
|
27
|
+
"update": "issue-basics",
|
|
28
|
+
"list": "issue-basics",
|
|
29
|
+
"skill": "using-forge",
|
|
30
|
+
|
|
31
|
+
"worktree": "worktree",
|
|
32
|
+
"clean": "worktree",
|
|
33
|
+
"orphans": { "exempt": "full ownership (command-name routing + `forge issue orphans` guidance in the issue-basics body) pending — issue e1e33cf8; issue-basics does not yet document it, so mapping now would be a hollow claim" },
|
|
34
|
+
"rollback": "rollback",
|
|
35
|
+
"review": "review",
|
|
36
|
+
"verify": "verify",
|
|
37
|
+
"check": { "exempt": "legacy alias for `validate` (renamed in PR #50) — bin/forge-cmd.js VALID_COMMANDS only; owned by the `validate` skill" },
|
|
38
|
+
"docs": { "exempt": "bin/forge.js-direct; AGENTS.md/docs management — owning skill pending (coverage pass, plan §3.2)" },
|
|
39
|
+
"reset": "setup",
|
|
40
|
+
"reinstall": "setup",
|
|
41
|
+
|
|
42
|
+
"gate": "gates",
|
|
43
|
+
"control": "gates",
|
|
44
|
+
"doc-gate": "gates",
|
|
45
|
+
|
|
46
|
+
"adapter": { "exempt": "extensions skill pending (plan §3.2 B: new `extensions` skill)" },
|
|
47
|
+
"add": { "exempt": "extensions skill pending (plan §3.2 B: new `extensions` skill)" },
|
|
48
|
+
"new": { "exempt": "extensions skill pending (plan §3.2 B: new `extensions` skill)" },
|
|
49
|
+
"audit": { "exempt": "extensions skill pending (plan §3.2 B: new `extensions` skill)" },
|
|
50
|
+
"role": { "exempt": "extensions skill pending — role/skill binding (plan §3.2 B)" },
|
|
51
|
+
|
|
52
|
+
"export": "portability",
|
|
53
|
+
"migrate": "portability",
|
|
54
|
+
"sync": { "exempt": "cloud-native external-platform sync (ADR-0004); a local no-op with no value until a server backend is configured — skill deferred (issue 666f4b4c)" },
|
|
55
|
+
|
|
56
|
+
"inbox": { "exempt": "team skill pending (plan §3.2 B: new `team` skill)" },
|
|
57
|
+
"serve": { "exempt": "team skill pending (plan §3.2 B: new `team` skill)" },
|
|
58
|
+
"team": { "exempt": "team skill pending (plan §3.2 B: new `team` skill)" },
|
|
59
|
+
|
|
60
|
+
"setup": "setup",
|
|
61
|
+
"init": "setup",
|
|
62
|
+
"upgrade": "setup",
|
|
63
|
+
"doctor": "setup",
|
|
64
|
+
"hooks": "setup",
|
|
65
|
+
"recommend": "setup",
|
|
66
|
+
|
|
67
|
+
"push": { "exempt": "ship skill push/preflight extension pending (plan §3.2 B)" },
|
|
68
|
+
"preflight": { "exempt": "ship skill push/preflight extension pending (plan §3.2 B)" },
|
|
69
|
+
"pr": { "exempt": "ship/shepherd PR-lifecycle extension pending (plan §3.2)" },
|
|
70
|
+
"merge": { "exempt": "ship/shepherd PR-lifecycle extension pending (plan §3.2)" },
|
|
71
|
+
|
|
72
|
+
"patch": { "exempt": "kernel skill fold pending (plan §3.2 C: fold into `kernel`)" },
|
|
73
|
+
"stage": { "exempt": "kernel skill fold pending (plan §3.2 C: fold into `kernel`)" },
|
|
74
|
+
"explain": { "exempt": "kernel skill fold pending (plan §3.2 C: fold into `kernel`)" },
|
|
75
|
+
"options": { "exempt": "kernel skill fold pending (plan §3.2 C: fold into `kernel`)" },
|
|
76
|
+
"insights": { "exempt": "kernel skill fold pending — kernel evidence inspection (plan §3.2 C)" },
|
|
77
|
+
|
|
78
|
+
"lint": { "exempt": "internal quality plumbing; skill assignment pending (coverage pass)" },
|
|
79
|
+
"release": { "exempt": "internal release plumbing; skill assignment pending (coverage pass)" },
|
|
80
|
+
"test": { "exempt": "internal test-runner plumbing; skill assignment pending (coverage pass)" },
|
|
81
|
+
"stale": { "exempt": "internal stale-detection plumbing; skill assignment pending (coverage pass)" }
|
|
82
|
+
}
|
|
83
|
+
}
|
package/skills/dev/SKILL.md
CHANGED
|
@@ -13,12 +13,16 @@ description: >
|
|
|
13
13
|
opening the PR (ship), for addressing PR review feedback (review), or for orchestrating
|
|
14
14
|
several stages / taking an issue end-to-end to a merged PR (smith).
|
|
15
15
|
allowed-tools: Bash, Read, Write, Edit, Grep, Glob
|
|
16
|
+
next: validate
|
|
17
|
+
terminal: false
|
|
16
18
|
---
|
|
17
19
|
|
|
18
20
|
Implement each task from the /plan task list using a subagent-driven loop: implementer → spec compliance reviewer → code quality reviewer per task.
|
|
19
21
|
|
|
20
22
|
# Dev
|
|
21
23
|
|
|
24
|
+
> **Chain (HARD-GATE):** the ONLY skill you invoke after `dev` is `validate`. Never open a PR straight from `dev`.
|
|
25
|
+
|
|
22
26
|
This skill reads the task list created by `/plan` and implements each task using a three-stage subagent loop. TDD is enforced inside each implementer subagent.
|
|
23
27
|
|
|
24
28
|
## Usage
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill": "dev",
|
|
3
|
+
"fixtures": "present",
|
|
4
|
+
"static": {
|
|
5
|
+
"token_cost": {
|
|
6
|
+
"desc_chars": 962,
|
|
7
|
+
"body_lines": 328,
|
|
8
|
+
"score": 20
|
|
9
|
+
},
|
|
10
|
+
"caps": {
|
|
11
|
+
"desc_within": true,
|
|
12
|
+
"body_within": true,
|
|
13
|
+
"score": 100
|
|
14
|
+
},
|
|
15
|
+
"description_quality": {
|
|
16
|
+
"has_trigger_cues": true,
|
|
17
|
+
"has_disambiguation_cues": true,
|
|
18
|
+
"adequate_length": true,
|
|
19
|
+
"score": 100
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"router_reachability": {
|
|
23
|
+
"has_curated_rule": true,
|
|
24
|
+
"router_exempt": false,
|
|
25
|
+
"fixtures": "present",
|
|
26
|
+
"fixtures_total": 6,
|
|
27
|
+
"fixtures_best_hit": 2,
|
|
28
|
+
"reachable": true,
|
|
29
|
+
"keyword_alignment": 0.33
|
|
30
|
+
},
|
|
31
|
+
"behavioral": {
|
|
32
|
+
"trigger_recall": null,
|
|
33
|
+
"trigger_precision": null,
|
|
34
|
+
"disambiguation": null,
|
|
35
|
+
"chain_correctness": null,
|
|
36
|
+
"outcome_quality": null,
|
|
37
|
+
"variance": null,
|
|
38
|
+
"note": "behavioral — W5 (LLM judge): semantic recall/precision/chain/outcome/variance, not deterministic"
|
|
39
|
+
},
|
|
40
|
+
"composite": 76
|
|
41
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gates
|
|
3
|
+
description: >
|
|
4
|
+
Toggle Forge's workflow gates and rails — strong-but-toggleable enforcement. `forge gate
|
|
5
|
+
enable|disable <gate-id>` flips `workflow.gates.<id>.enabled` in `.forge/config.yaml`; the
|
|
6
|
+
installed git hooks read resolved config at run time, so disabling a rail makes them
|
|
7
|
+
genuinely inert. `forge gate approve|reject <issue> <gate>` records durable human-gate
|
|
8
|
+
approval events; `forge gate status`/`check` query them; `forge control <id>
|
|
9
|
+
<mandatory|optional|permission>` sets tri-state DECLARED intent (writes the same `enabled`
|
|
10
|
+
field — no independent runtime enforcement). Use when the user says "disable the gate",
|
|
11
|
+
"turn off TDD enforcement", "the tdd intent rail is blocking me", "toggle or enable a
|
|
12
|
+
gate", "loosen enforcement", or "approve a human gate". Common default-ON toggleable rails:
|
|
13
|
+
`rail.tdd_intent`, `rail.kernel_tracking`, `rail.auto_shepherd` — e.g. `forge gate disable
|
|
14
|
+
rail.tdd_intent`. NOT for addressing PR review feedback (review), NOT the status snapshot
|
|
15
|
+
of work in flight (status).
|
|
16
|
+
allowed-tools: Bash, Read, Grep, Glob
|
|
17
|
+
terminal: true
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
Forge's gates and rails are **default-strong but toggleable**. The `gates` skill is how you flip one off (or back on), record a human-gate approval, and see enforcement state. The stored truth is one field — `workflow.gates.<id>.enabled` — and the installed hooks read the resolved config at run time, so a disabled rail is genuinely inert, not cosmetically off.
|
|
21
|
+
|
|
22
|
+
# Toggling gates and rails
|
|
23
|
+
|
|
24
|
+
## When to use
|
|
25
|
+
|
|
26
|
+
- "Disable the gate", "turn off TDD enforcement", "the tdd intent rail is blocking me".
|
|
27
|
+
- "Toggle / enable a gate", "loosen enforcement for this repo".
|
|
28
|
+
- "Approve a human gate" (record a durable approval event on an issue).
|
|
29
|
+
|
|
30
|
+
## Toggle a gate or rail (the enforcement switch)
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
forge gate disable <gate-id> # set workflow.gates.<id>.enabled = false in .forge/config.yaml
|
|
34
|
+
forge gate enable <gate-id> # set it back to true
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
An unknown gate id — or disabling a **locked** gate — errors **before** anything is written, never mid-run. Because the hooks resolve config at run time, the flip takes effect immediately with no reinstall.
|
|
38
|
+
|
|
39
|
+
### Common rails (default-ON, toggleable)
|
|
40
|
+
|
|
41
|
+
| Rail | What it enforces | Turn off with |
|
|
42
|
+
| --- | --- | --- |
|
|
43
|
+
| `rail.tdd_intent` | Pre-commit TDD gate (source changed ⇒ tests changed). The `minimal` adoption profile ships it off. | `forge gate disable rail.tdd_intent` |
|
|
44
|
+
| `rail.kernel_tracking` | "File every issue" — nothing discussed goes missing. | `forge gate disable rail.kernel_tracking` |
|
|
45
|
+
| `rail.auto_shepherd` | The autonomous PR-shepherd daemon fire. | `forge gate disable rail.auto_shepherd` |
|
|
46
|
+
|
|
47
|
+
The `gate.*` and `rail.*` id namespaces are disjoint, so `forge gate enable|disable` governs both through one flat surface.
|
|
48
|
+
|
|
49
|
+
## Human-gate approval events
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
forge gate approve <issue-id> <gate-id> # record a durable gate.approved event
|
|
53
|
+
forge gate reject <issue-id> <gate-id> # record gate.rejected
|
|
54
|
+
forge gate status <issue-id> # list recorded events for that issue (resume-safe)
|
|
55
|
+
forge gate check <issue-id> <gate-id> # exit 0 iff gate DISABLED or an approval exists on that issue
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`check` is the reusable enforcement primitive a stage skill calls: it passes when the gate is disabled or an approval event has been recorded for that specific issue id (approvals are issue-scoped, so pass the same `<issue-id>` you approved against). Events are durable on the issue, so they survive a compaction or crash.
|
|
59
|
+
|
|
60
|
+
## Tri-state control (declared intent)
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
forge control <gate-id|rail-id> <mandatory|optional|permission>
|
|
64
|
+
forge control status [--json]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`forge control` sets the **declared intent** vocabulary and writes the **same** `enabled` field that `forge gate` writes — there is deliberately no parallel key. It is a view/intent layer: today no runtime consumer denies purely on a control flag (MCP/rules/skills are presence-only and refused). For actually turning enforcement off, use `forge gate disable`.
|
|
68
|
+
|
|
69
|
+
## The doc-update gate
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
forge gate doc <detect|check|init|...> # = forge doc-gate (run `forge doc-gate --help`)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The doc-update gate folds under this noun as `forge gate doc`; bare `forge doc-gate` stays as a back-compat alias.
|
|
76
|
+
|
|
77
|
+
## Adjacent skills
|
|
78
|
+
|
|
79
|
+
- Addressing PR review feedback / resolving threads → `review`.
|
|
80
|
+
- The snapshot of where the project stands and what's in flight → `status`.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"query": "Disable the tdd gate for this repo.",
|
|
4
|
+
"should_trigger": true
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
"query": "Turn off TDD enforcement — the tdd intent rail is blocking my commit.",
|
|
8
|
+
"should_trigger": true
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"query": "Toggle the kernel tracking rail off for now.",
|
|
12
|
+
"should_trigger": true
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"query": "Enable a gate that I disabled earlier and loosen enforcement.",
|
|
16
|
+
"should_trigger": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"query": "Approve the human gate on this issue so the stage can proceed.",
|
|
20
|
+
"should_trigger": true
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"query": "Disable the auto shepherd rail so no PR daemon fires.",
|
|
24
|
+
"should_trigger": true
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"query": "Address the CodeRabbit review feedback and resolve the threads.",
|
|
28
|
+
"should_trigger": false
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"query": "Where am I in the workflow and what work is in flight right now?",
|
|
32
|
+
"should_trigger": false
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"query": "Run the tests and lint before I push.",
|
|
36
|
+
"should_trigger": false
|
|
37
|
+
}
|
|
38
|
+
]
|