forge-workflow 0.1.0-beta.5 → 0.1.0-beta.7
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 +48 -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 +120 -9
- 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 +78 -22
- 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/validation-receipt.js +190 -0
- package/lib/workflow/enforce-stage.js +44 -0
- package/lib/workflow/plan-authority.js +225 -0
- package/node_modules/@forge/contracts/compatibility-matrix.v1.json +1 -0
- package/node_modules/@forge/contracts/contract-baseline.v1.json +134 -0
- package/node_modules/@forge/contracts/fixtures/v1/canonical-hash.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/contract-inputs.v1.json +71 -0
- package/node_modules/@forge/contracts/fixtures/v1/feedback-secret-reject.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/identity-conflict.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/malformed-envelope-inputs.v1.json +16 -0
- package/node_modules/@forge/contracts/fixtures/v1/malformed-missing-required.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/monitor-bounds-reject.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/monitor-receipt-privacy-reject.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/not-executed-receipt.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/privacy-redaction.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/retry-identical.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/stale-authority-reject.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/structured-error-privacy-reject.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-live-reject.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-test-reject.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/unknown-advisory-roundtrip.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/unknown-consequential-reject.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/valid-full.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/valid-minimal.json +1 -0
- package/node_modules/@forge/contracts/fixtures/v1/wrong-capability-digest.json +1 -0
- package/node_modules/@forge/contracts/index.js +32 -0
- package/node_modules/@forge/contracts/package.json +35 -0
- package/node_modules/@forge/contracts/schemas/v1/capability-manifest.schema.json +1 -0
- package/node_modules/@forge/contracts/schemas/v1/claim-request.schema.json +1 -0
- package/node_modules/@forge/contracts/schemas/v1/context-packet.schema.json +1 -0
- package/node_modules/@forge/contracts/schemas/v1/delivery-receipt.schema.json +1 -0
- package/node_modules/@forge/contracts/schemas/v1/feedback-report.schema.json +1 -0
- package/node_modules/@forge/contracts/schemas/v1/lease-receipt.schema.json +1 -0
- package/node_modules/@forge/contracts/schemas/v1/monitor-event.schema.json +1 -0
- package/node_modules/@forge/contracts/schemas/v1/monitor-receipt.schema.json +1 -0
- package/node_modules/@forge/contracts/schemas/v1/run-receipt.schema.json +1 -0
- package/node_modules/@forge/contracts/schemas/v1/structured-error.schema.json +1 -0
- package/node_modules/@forge/contracts/schemas/v1/work-packet.schema.json +1 -0
- package/node_modules/@forge/contracts/src/baseline.js +23 -0
- package/node_modules/@forge/contracts/src/canonical.js +151 -0
- package/node_modules/@forge/contracts/src/definitions.js +176 -0
- package/node_modules/@forge/contracts/src/identity.js +42 -0
- package/node_modules/@forge/contracts/src/schema.js +72 -0
- package/node_modules/@forge/contracts/src/validate.js +305 -0
- package/node_modules/@forge/flow/index.js +114 -0
- package/node_modules/@forge/flow/package.json +34 -0
- package/node_modules/@forge/flow/src/bounded-loop.js +415 -0
- package/node_modules/@forge/flow/src/efficiency-supervisor.js +89 -0
- package/node_modules/@forge/flow/src/executor.js +279 -0
- package/node_modules/@forge/flow/src/monitor-durability.js +419 -0
- package/node_modules/@forge/flow/src/monitor-runtime.js +460 -0
- package/node_modules/@forge/flow/src/process-lifecycle.js +469 -0
- package/node_modules/@forge/flow/src/skill-runtime.js +343 -0
- package/node_modules/@forge/memory/index.js +331 -0
- package/node_modules/@forge/memory/package.json +34 -0
- package/node_modules/@forge/memory/src/authority-provider.js +67 -0
- package/node_modules/@forge/memory/src/backend-registry.js +166 -0
- package/node_modules/@forge/memory/src/feedback-intake.js +243 -0
- package/node_modules/@forge/memory/src/pr-lifecycle-authority.js +946 -0
- package/node_modules/@forge/memory/src/usage-evidence.js +205 -0
- package/package.json +16 -4
- package/packages/flow/node_modules/@forge/contracts/compatibility-matrix.v1.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/contract-baseline.v1.json +134 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/canonical-hash.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/contract-inputs.v1.json +71 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/feedback-secret-reject.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/identity-conflict.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/malformed-envelope-inputs.v1.json +16 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/malformed-missing-required.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/monitor-bounds-reject.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/monitor-receipt-privacy-reject.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/not-executed-receipt.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/privacy-redaction.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/retry-identical.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/stale-authority-reject.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/structured-error-privacy-reject.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-live-reject.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-test-reject.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/unknown-advisory-roundtrip.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/unknown-consequential-reject.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/valid-full.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/valid-minimal.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/fixtures/v1/wrong-capability-digest.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/index.js +32 -0
- package/packages/flow/node_modules/@forge/contracts/package.json +35 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/capability-manifest.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/claim-request.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/context-packet.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/delivery-receipt.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/feedback-report.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/lease-receipt.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/monitor-event.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/monitor-receipt.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/run-receipt.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/structured-error.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/schemas/v1/work-packet.schema.json +1 -0
- package/packages/flow/node_modules/@forge/contracts/src/baseline.js +23 -0
- package/packages/flow/node_modules/@forge/contracts/src/canonical.js +151 -0
- package/packages/flow/node_modules/@forge/contracts/src/definitions.js +176 -0
- package/packages/flow/node_modules/@forge/contracts/src/identity.js +42 -0
- package/packages/flow/node_modules/@forge/contracts/src/schema.js +72 -0
- package/packages/flow/node_modules/@forge/contracts/src/validate.js +305 -0
- package/packages/memory/node_modules/@forge/contracts/compatibility-matrix.v1.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/contract-baseline.v1.json +134 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/canonical-hash.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/contract-inputs.v1.json +71 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/feedback-secret-reject.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/identity-conflict.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/malformed-envelope-inputs.v1.json +16 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/malformed-missing-required.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/monitor-bounds-reject.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/monitor-receipt-privacy-reject.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/not-executed-receipt.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/privacy-redaction.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/retry-identical.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/stale-authority-reject.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/structured-error-privacy-reject.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-live-reject.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/structured-error-stripe-test-reject.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/unknown-advisory-roundtrip.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/unknown-consequential-reject.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/valid-full.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/valid-minimal.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/fixtures/v1/wrong-capability-digest.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/index.js +32 -0
- package/packages/memory/node_modules/@forge/contracts/package.json +35 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/capability-manifest.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/claim-request.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/context-packet.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/delivery-receipt.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/feedback-report.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/lease-receipt.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/monitor-event.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/monitor-receipt.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/run-receipt.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/structured-error.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/schemas/v1/work-packet.schema.json +1 -0
- package/packages/memory/node_modules/@forge/contracts/src/baseline.js +23 -0
- package/packages/memory/node_modules/@forge/contracts/src/canonical.js +151 -0
- package/packages/memory/node_modules/@forge/contracts/src/definitions.js +176 -0
- package/packages/memory/node_modules/@forge/contracts/src/identity.js +42 -0
- package/packages/memory/node_modules/@forge/contracts/src/schema.js +72 -0
- package/packages/memory/node_modules/@forge/contracts/src/validate.js +305 -0
- 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 +96 -15
- 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/lib/commands/ship.js
CHANGED
|
@@ -14,6 +14,7 @@ const path = require('node:path');
|
|
|
14
14
|
|
|
15
15
|
const { fireAndForget } = require('../pr-monitor/reconcile-executor');
|
|
16
16
|
const { getResolvedRuntimeGraph } = require('../core/runtime-graph');
|
|
17
|
+
const { resolveBaseRemote, resolveBaseBranch } = require('../base-remote');
|
|
17
18
|
|
|
18
19
|
const AUTO_SHEPHERD_RAIL = 'rail.auto_shepherd';
|
|
19
20
|
|
|
@@ -65,72 +66,6 @@ function getQuietGitExecOptions(cwd = process.cwd()) {
|
|
|
65
66
|
return { ...getGitExecOptions(cwd), stdio: 'pipe' };
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
function resolveRemoteHeadTarget(exec = execFileSync, cwd = process.cwd(), remoteName) {
|
|
69
|
-
try {
|
|
70
|
-
const symbolicRef = exec('git', ['symbolic-ref', `refs/remotes/${remoteName}/HEAD`], getQuietGitExecOptions(cwd)).trim();
|
|
71
|
-
if (!symbolicRef) {
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
exec('git', ['rev-parse', '--verify', symbolicRef], getQuietGitExecOptions(cwd));
|
|
75
|
-
return symbolicRef;
|
|
76
|
-
} catch (_error) {
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function remoteHasTrackingBase(exec = execFileSync, cwd = process.cwd(), remoteName) {
|
|
82
|
-
if (resolveRemoteHeadTarget(exec, cwd, remoteName)) {
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
for (const candidate of ['main', 'master']) {
|
|
87
|
-
try {
|
|
88
|
-
exec('git', ['rev-parse', '--verify', `refs/remotes/${remoteName}/${candidate}`], getQuietGitExecOptions(cwd));
|
|
89
|
-
return true;
|
|
90
|
-
} catch (_error) {
|
|
91
|
-
// Probe next candidate.
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function resolveBaseRemote(exec = execFileSync, cwd = process.cwd()) {
|
|
99
|
-
for (const candidate of ['upstream', 'origin']) {
|
|
100
|
-
try {
|
|
101
|
-
exec('git', ['remote', 'get-url', candidate], getQuietGitExecOptions(cwd));
|
|
102
|
-
if (remoteHasTrackingBase(exec, cwd, candidate)) {
|
|
103
|
-
return candidate;
|
|
104
|
-
}
|
|
105
|
-
} catch (_error) {
|
|
106
|
-
// Probe next candidate.
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return 'origin';
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function resolveBaseBranch(exec = execFileSync, cwd = process.cwd(), remoteName = resolveBaseRemote(exec, cwd)) {
|
|
114
|
-
const symbolicRef = resolveRemoteHeadTarget(exec, cwd, remoteName);
|
|
115
|
-
if (symbolicRef) {
|
|
116
|
-
const match = new RegExp(`^refs/remotes/${remoteName}/(.+)$`).exec(symbolicRef);
|
|
117
|
-
if (match && match[1]) {
|
|
118
|
-
return match[1];
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
for (const candidate of ['main', 'master']) {
|
|
123
|
-
try {
|
|
124
|
-
exec('git', ['rev-parse', '--verify', `refs/remotes/${remoteName}/${candidate}`], getQuietGitExecOptions(cwd));
|
|
125
|
-
return candidate;
|
|
126
|
-
} catch (_error) {
|
|
127
|
-
// Probe next candidate.
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return 'master';
|
|
132
|
-
}
|
|
133
|
-
|
|
134
69
|
function refreshBaseReference(exec = execFileSync, cwd = process.cwd(), remoteName, baseBranch) {
|
|
135
70
|
exec(
|
|
136
71
|
'git',
|
|
@@ -457,10 +392,13 @@ function validatePRTitle(title) {
|
|
|
457
392
|
|
|
458
393
|
async function createPR(options) { // NOSONAR S3776
|
|
459
394
|
const { title, body, dryRun = false, exec = execFileSync, cwd = process.cwd() } = options;
|
|
395
|
+
const gh = options.githubContext?.bound
|
|
396
|
+
? (args, runOptions) => options.githubContext.runGh(args, runOptions)
|
|
397
|
+
: (args, runOptions) => exec('gh', args, runOptions);
|
|
460
398
|
const titleValidation = validatePRTitle(title);
|
|
461
399
|
if (!titleValidation.valid) return { success: false, error: titleValidation.error };
|
|
462
400
|
try {
|
|
463
|
-
|
|
401
|
+
gh(['--version'], { ...getGhCheckOptions(), cwd });
|
|
464
402
|
} catch (error) {
|
|
465
403
|
if (isCommandNotFound(error)) {
|
|
466
404
|
return { success: false, error: 'GitHub CLI (gh) not found. Install from: https://cli.github.com/' };
|
|
@@ -495,7 +433,7 @@ async function createPR(options) { // NOSONAR S3776
|
|
|
495
433
|
return { success: true, message: '[DRY RUN] Would create PR with title: ' + title, prUrl: 'https://github.com/owner/repo/pull/1' };
|
|
496
434
|
}
|
|
497
435
|
try {
|
|
498
|
-
const result =
|
|
436
|
+
const result = gh(['pr', 'create', '--title', title, '--body', body], getGitExecOptions(cwd));
|
|
499
437
|
const urlMatch = /https:\/\/github\.com\/[^\s]+/.exec(result);
|
|
500
438
|
const prUrl = urlMatch ? urlMatch[0] : null;
|
|
501
439
|
const numberMatch = /\/pull\/(\d+)/.exec(result);
|
|
@@ -585,7 +523,10 @@ async function executeShip(options) {
|
|
|
585
523
|
testScenarios,
|
|
586
524
|
coverage,
|
|
587
525
|
});
|
|
588
|
-
const result = await createPr({
|
|
526
|
+
const result = await createPr({
|
|
527
|
+
title, body: prBody, dryRun,
|
|
528
|
+
exec: options.exec, cwd: options.cwd || projectRoot, githubContext: options.githubContext,
|
|
529
|
+
});
|
|
589
530
|
if (!result.success) return result;
|
|
590
531
|
maybeTriggerShepherdAfterShip({ dryRun, projectRoot, fireAndForget: trigger });
|
|
591
532
|
return {
|
|
@@ -601,6 +542,7 @@ async function executeShip(options) {
|
|
|
601
542
|
|
|
602
543
|
module.exports = {
|
|
603
544
|
name: 'ship',
|
|
545
|
+
githubAuth: true,
|
|
604
546
|
description: 'Create a pull request from validated feature work',
|
|
605
547
|
handler: async (args, flags = {}, projectRoot = process.cwd(), deps = {}) => {
|
|
606
548
|
const result = await executeShip({
|
|
@@ -609,6 +551,10 @@ module.exports = {
|
|
|
609
551
|
dryRun: Boolean(flags.dryRun || flags['--dry-run']),
|
|
610
552
|
projectRoot,
|
|
611
553
|
fireAndForget: deps.fireAndForget,
|
|
554
|
+
exec: deps.exec,
|
|
555
|
+
cwd: deps.cwd || projectRoot,
|
|
556
|
+
githubContext: deps.githubContext,
|
|
557
|
+
createPr: deps.createPr,
|
|
612
558
|
});
|
|
613
559
|
if (!result.success) {
|
|
614
560
|
return result;
|
package/lib/commands/skill.js
CHANGED
|
@@ -23,6 +23,7 @@ const { runBehavioralEvaluation } = require('../../scripts/lib/behavioral-eval-r
|
|
|
23
23
|
const { resolveBehavioralEvaluation } = require('../../scripts/lib/behavioral-eval-runtime');
|
|
24
24
|
const { loadPromotionEvidence } = require('../../scripts/lib/promotion-evidence-loader');
|
|
25
25
|
const { scorePromotion } = require('../../scripts/lib/promotion-scorecard');
|
|
26
|
+
const { createGithubContext } = require('../github-context');
|
|
26
27
|
|
|
27
28
|
const BEHAVIORAL_TIERS = Object.freeze([30, 100, 300]);
|
|
28
29
|
const BEHAVIORAL_TIER_VALUES = new Set(BEHAVIORAL_TIERS.map(String));
|
|
@@ -158,12 +159,19 @@ async function handleFullEval(rest, flags, projectRoot, opts) {
|
|
|
158
159
|
let behavioralOptions = opts.behavioralOptions || {};
|
|
159
160
|
if (!opts.runBehavioralEvaluation || opts.resolveBehavioralEvaluation) {
|
|
160
161
|
const resolver = opts.resolveBehavioralEvaluation || resolveBehavioralEvaluation;
|
|
162
|
+
let githubContext;
|
|
163
|
+
try {
|
|
164
|
+
githubContext = (opts.prepareGithubContext || createGithubContext)(projectRoot);
|
|
165
|
+
} catch {
|
|
166
|
+
return { success: false, error: 'Unable to prepare GitHub account context. Run forge github status for guidance.' };
|
|
167
|
+
}
|
|
161
168
|
const resolved = await resolver({
|
|
162
169
|
projectRoot,
|
|
163
170
|
skillName: name,
|
|
164
171
|
skillPath: path.join(ctx.skillsDir, name, 'SKILL.md'),
|
|
165
172
|
tier,
|
|
166
173
|
env: opts.env || process.env,
|
|
174
|
+
...(githubContext.bound ? { deps: { runGh: githubContext.runGh } } : {}),
|
|
167
175
|
});
|
|
168
176
|
if (!resolved.ok) return formatResult(scoreResult(resolved.result));
|
|
169
177
|
behavioralOptions = { ...resolved.options, ...behavioralOptions };
|
package/lib/commands/team.js
CHANGED
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
const { existsSync } = require('node:fs');
|
|
2
2
|
const { execFileSync } = require('node:child_process');
|
|
3
3
|
const path = require('node:path');
|
|
4
|
-
const { getPackageRoot } = require('../package-root');
|
|
4
|
+
const { getPackageRoot, isCompiledBinary } = require('../package-root');
|
|
5
|
+
const { firstPositionalIndex, stripGlobalFlags } = require('../global-flags');
|
|
6
|
+
|
|
7
|
+
function teamArguments(args) {
|
|
8
|
+
const subcommandIndex = firstPositionalIndex(args);
|
|
9
|
+
if (subcommandIndex === -1) return stripGlobalFlags(args);
|
|
10
|
+
const result = stripGlobalFlags(args.slice(0, subcommandIndex));
|
|
11
|
+
// Path is routing metadata anywhere; other flags after the subcommand belong to Team.
|
|
12
|
+
for (let index = subcommandIndex; index < args.length; index++) {
|
|
13
|
+
const arg = args[index];
|
|
14
|
+
if (arg.startsWith('--path=')) continue;
|
|
15
|
+
if (arg === '-p' || arg === '--path') {
|
|
16
|
+
if (args[index + 1] !== undefined && !args[index + 1].startsWith('-')) index++;
|
|
17
|
+
} else {
|
|
18
|
+
result.push(arg);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
5
23
|
|
|
6
24
|
// Resolve bash binary from fixed, known locations only (no PATH search).
|
|
7
25
|
// SonarCloud S4036: PATH must not be searched for OS commands.
|
|
@@ -22,28 +40,49 @@ function _resolveBash() {
|
|
|
22
40
|
return '/usr/bin/bash';
|
|
23
41
|
}
|
|
24
42
|
|
|
25
|
-
function handleTeam(args) {
|
|
43
|
+
function handleTeam(args, projectRoot = process.cwd(), deps = {}) {
|
|
26
44
|
// Route through the asset root so `forge team` finds the script in BOTH channels:
|
|
27
45
|
// the on-disk package (npm/npx) and a compiled binary (extracted embed dir).
|
|
28
|
-
const
|
|
46
|
+
const packageRoot = getPackageRoot();
|
|
47
|
+
const scriptPath = path.join(packageRoot, 'scripts', 'forge-team', 'index.sh');
|
|
29
48
|
const bashPath = _resolveBash();
|
|
49
|
+
const runFile = deps.execFileSync || execFileSync;
|
|
50
|
+
const options = { stdio: 'inherit' };
|
|
51
|
+
if (deps.githubContext?.bound) {
|
|
52
|
+
// Bash also runs Git/Forge/helpers. Only the re-entering gh bridge may resolve credentials.
|
|
53
|
+
const compiled = (deps.isCompiledBinary || isCompiledBinary)();
|
|
54
|
+
const env = { ...(deps.env || process.env) };
|
|
55
|
+
for (const key of Object.keys(env)) {
|
|
56
|
+
if (['gh_token', 'github_token', 'gh_host'].includes(key.toLowerCase())) delete env[key];
|
|
57
|
+
}
|
|
58
|
+
options.cwd = projectRoot;
|
|
59
|
+
options.env = {
|
|
60
|
+
...env,
|
|
61
|
+
GH_CMD: path.join(packageRoot, 'scripts', 'github-context-bridge.sh').replaceAll('\\', '/'),
|
|
62
|
+
FORGE_GITHUB_RUNTIME: process.execPath.replaceAll('\\', '/'),
|
|
63
|
+
FORGE_GITHUB_ENTRY: compiled ? '' : path.join(packageRoot, 'bin', 'forge.js').replaceAll('\\', '/'),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
30
66
|
|
|
31
67
|
try {
|
|
32
|
-
|
|
33
|
-
stdio: 'inherit',
|
|
34
|
-
});
|
|
68
|
+
runFile(bashPath, [scriptPath, ...teamArguments(args)], options);
|
|
35
69
|
} catch (err) {
|
|
36
70
|
process.exit(err.status || 1);
|
|
37
71
|
}
|
|
38
72
|
}
|
|
39
73
|
|
|
40
|
-
async function handler(args =
|
|
41
|
-
handleTeam(args);
|
|
74
|
+
async function handler(args, _flags, projectRoot, deps = {}) {
|
|
75
|
+
handleTeam(args || [], projectRoot, deps);
|
|
42
76
|
return { success: true };
|
|
43
77
|
}
|
|
44
78
|
|
|
45
79
|
module.exports = {
|
|
46
80
|
name: 'team',
|
|
81
|
+
githubAuth: (args = []) => {
|
|
82
|
+
const teamArgs = teamArguments(args);
|
|
83
|
+
return ['add', 'verify', 'sync', 'claim'].includes(teamArgs[0])
|
|
84
|
+
|| (teamArgs[0] === 'workload' && teamArgs.slice(1).some(arg => arg.replace(/\r$/, '') === '--me'));
|
|
85
|
+
},
|
|
47
86
|
description: 'Team coordination workflows and sync utilities',
|
|
48
87
|
handler,
|
|
49
88
|
handleTeam
|
package/lib/commands/test.js
CHANGED
|
@@ -32,6 +32,9 @@ const DIRECT_TEST_CANDIDATES = Object.freeze({
|
|
|
32
32
|
'test/cli-flags.test.js',
|
|
33
33
|
'test/forge-cli-registry.test.js',
|
|
34
34
|
'test/setup-runtime-flags.test.js',
|
|
35
|
+
'test/github-launcher.test.js',
|
|
36
|
+
'test/integration/github-account-context.test.js',
|
|
37
|
+
'test/structural/github-account-public-surface.test.js',
|
|
35
38
|
],
|
|
36
39
|
// The second CLI surface (command descriptions + shepherd wiring). Without an
|
|
37
40
|
// entry here it resolves to zero tests and pushes fall back to the full suite.
|
|
@@ -47,15 +50,143 @@ const DIRECT_TEST_CANDIDATES = Object.freeze({
|
|
|
47
50
|
'test/doc-assertions.test.js',
|
|
48
51
|
'test/scripts/doc-asserting-tests.test.js',
|
|
49
52
|
],
|
|
53
|
+
// The suites that assert the repo .gitignore's actual content. The path is
|
|
54
|
+
// already known to the classifier, so without candidates here it resolves
|
|
55
|
+
// zero tests and a one-line ignore addition pays the full-suite lane.
|
|
56
|
+
'.gitignore': [
|
|
57
|
+
'test/check-forge-token.test.js',
|
|
58
|
+
'test/cleanup/dropped-agent-config.test.js',
|
|
59
|
+
'test/coverage-config.test.js',
|
|
60
|
+
'test/eval/eval-history.test.js',
|
|
61
|
+
'test/freshness-token.test.js',
|
|
62
|
+
'test/mutation-config.test.js',
|
|
63
|
+
'test/test-dashboard.test.js',
|
|
64
|
+
],
|
|
50
65
|
'lib/lefthook-check.js': ['test/lefthook-check.test.js', 'test/runtime-health.test.js'],
|
|
51
66
|
'lib/runtime-health.js': ['test/runtime-health.test.js'],
|
|
52
67
|
// skill-eval hosts the accuracy-lint detectors (auditCommandDocumentation /
|
|
53
68
|
// auditRouterPrecision), so edits there must also run their detector suite.
|
|
54
69
|
'lib/skill-eval.js': ['test/skill-eval.test.js', 'test/skill-accuracy.test.js'],
|
|
70
|
+
// Schema is shared by every kernel authority; keep it on the full-suite lane.
|
|
71
|
+
'lib/kernel/schema.js': [],
|
|
72
|
+
// Migrations have consumers across the kernel; an empty mapping deliberately
|
|
73
|
+
// selects the full-suite lane, matching the shared SQLite driver.
|
|
74
|
+
'lib/kernel/migrations.js': [],
|
|
75
|
+
'lib/commands/shepherd.js': [
|
|
76
|
+
'test/commands/shepherd.test.js',
|
|
77
|
+
'test/commands/github-route-matrix.test.js',
|
|
78
|
+
'test/pr-monitor/arm-on-push.test.js',
|
|
79
|
+
'test/pr-monitor/shepherd-watch.test.js',
|
|
80
|
+
'test/pr-monitor/watch-owner-cutover.test.js',
|
|
81
|
+
'test/pr-monitor/watch-owner-launch-contention.test.js',
|
|
82
|
+
'test/pr-monitor/watch-owner-launch-gate.test.js',
|
|
83
|
+
],
|
|
84
|
+
'lib/commands/github.js': [
|
|
85
|
+
'test/commands/github.test.js',
|
|
86
|
+
'test/github-launcher.test.js',
|
|
87
|
+
'test/integration/github-account-context.test.js',
|
|
88
|
+
],
|
|
89
|
+
'lib/github-context.js': ['test/github-context.test.js', 'test/integration/github-account-context.test.js'],
|
|
90
|
+
'lib/commands/_registry.js': [
|
|
91
|
+
'test/commands/_registry.test.js',
|
|
92
|
+
'test/commands/_registry-github-context.test.js',
|
|
93
|
+
],
|
|
94
|
+
'lib/commands/pr.js': ['test/commands/pr.test.js', 'test/commands/github-route-matrix.test.js'],
|
|
95
|
+
'lib/commands/ship.js': ['test/commands/ship.test.js', 'test/commands/github-route-matrix.test.js'],
|
|
96
|
+
'lib/commands/merge.js': ['test/commands/merge.test.js', 'test/commands/github-route-matrix.test.js'],
|
|
97
|
+
'lib/commands/team.js': ['test/commands/team.test.js', 'test/commands/github-route-matrix.test.js'],
|
|
98
|
+
'lib/commands/clean.js': ['test/commands/clean.test.js', 'test/commands/github-route-matrix.test.js'],
|
|
99
|
+
'scripts/github-context-bridge.sh': ['test/commands/github-route-matrix.test.js'],
|
|
100
|
+
'lib/commands/push.js': ['test/commands/push.test.js', 'test/commands/github-indirect-routes.test.js'],
|
|
101
|
+
'lib/commands/hooks.js': ['test/commands/hooks.test.js', 'test/hooks-session-start.test.js', 'test/commands/github-indirect-routes.test.js'],
|
|
102
|
+
'lib/commands/serve.js': ['test/commands/serve.test.js', 'test/commands/github-indirect-routes.test.js'],
|
|
103
|
+
'lib/commands/skill.js': ['test/commands/skill.test.js', 'test/commands/github-indirect-routes.test.js'],
|
|
104
|
+
'scripts/lib/behavioral-eval-runtime.js': ['test/eval/behavioral-eval-runtime.test.js', 'test/commands/github-indirect-routes.test.js'],
|
|
105
|
+
'web/dashboard/generate-snapshot.mjs': ['test/commands/github-indirect-routes.test.js'],
|
|
106
|
+
// The shared SQLite driver has more consumers than a bounded direct list can
|
|
107
|
+
// safely represent; its empty mapping deliberately selects the full-suite lane.
|
|
108
|
+
'lib/kernel/sqlite-driver.js': [],
|
|
109
|
+
'lib/pr-monitor/watch-owner.js': [
|
|
110
|
+
'test/pr-monitor/watch-owner.test.js',
|
|
111
|
+
'test/kernel/watch-owner-transaction.test.js',
|
|
112
|
+
],
|
|
113
|
+
'lib/pr-monitor/reconcile.js': [
|
|
114
|
+
'test/pr-monitor/reconcile.test.js',
|
|
115
|
+
'test/pr-monitor/reconcile-owner-authority.test.js',
|
|
116
|
+
],
|
|
117
|
+
'lib/pr-monitor/reconcile-executor.js': [
|
|
118
|
+
'test/commands/github-indirect-routes.test.js',
|
|
119
|
+
'test/pr-monitor/auto-trigger-containment.test.js',
|
|
120
|
+
'test/pr-monitor/reconcile-executor-owner-authority.test.js',
|
|
121
|
+
'test/pr-monitor/reconcile-executor.test.js',
|
|
122
|
+
'test/pr-monitor/reconcile-daemon-owner-authority.test.js',
|
|
123
|
+
'test/pr-monitor/reconcile-migration-gate.test.js',
|
|
124
|
+
'test/pr-monitor/watch-owner-launch-contention.test.js',
|
|
125
|
+
'test/windows-hide-background-spawns.test.js',
|
|
126
|
+
],
|
|
127
|
+
'lib/pr-monitor/watch.js': [
|
|
128
|
+
'test/pr-monitor/watch-owner-cutover.test.js',
|
|
129
|
+
'test/pr-monitor/watch.test.js',
|
|
130
|
+
],
|
|
131
|
+
'lib/pr-monitor/watch-lifecycle.js': [
|
|
132
|
+
'test/commands/github-indirect-routes.test.js',
|
|
133
|
+
'test/pr-monitor/watch-lifecycle.test.js',
|
|
134
|
+
'test/pr-monitor/watch-owner-launch-contention.test.js',
|
|
135
|
+
'test/pr-monitor/watch-owner-launch-gate.test.js',
|
|
136
|
+
],
|
|
137
|
+
'lib/pr-monitor/shepherd-lease.js': [
|
|
138
|
+
'test/pr-monitor/shepherd-lease.test.js',
|
|
139
|
+
'test/pr-monitor/shepherd-lease-election-only.test.js',
|
|
140
|
+
],
|
|
141
|
+
'lib/pr-monitor/monitor.js': [
|
|
142
|
+
'test/pr-monitor/monitor.test.js',
|
|
143
|
+
'test/pr-monitor/monitor-owner-signal.test.js',
|
|
144
|
+
],
|
|
55
145
|
'lib/upgrade-safety.js': ['test/commands/upgrade.test.js'],
|
|
56
146
|
'scripts/test.js': ['test/scripts/test-runner.test.js'],
|
|
147
|
+
// `bun.lock` is deliberately absent: a dependency change can break anything,
|
|
148
|
+
// so it stays on the full-suite lane.
|
|
149
|
+
// lefthook.yml is the repository's hook wiring, so this must also cover the
|
|
150
|
+
// suites that assert the individual pre-commit/pre-push commands exist.
|
|
151
|
+
'lefthook.yml': [
|
|
152
|
+
'test/lefthook-check.test.js',
|
|
153
|
+
'test/lefthook-wiring.test.js',
|
|
154
|
+
'test/commands/lefthook-user-config.test.js',
|
|
155
|
+
'test/branch-protection.test.js',
|
|
156
|
+
'test/commitlint.test.js',
|
|
157
|
+
'test/cross-platform-install.test.js',
|
|
158
|
+
'test/sync-d20-audit-script.test.js',
|
|
159
|
+
],
|
|
160
|
+
'eslint.config.js': ['test/lint-script.test.js'],
|
|
161
|
+
'.coderabbit.yaml': ['test/coderabbit-config.test.js'],
|
|
162
|
+
'.claude-plugin/marketplace.json': [
|
|
163
|
+
'test/activation/plugin-scaffold.test.js',
|
|
164
|
+
'test/plugin-catalog.test.js',
|
|
165
|
+
],
|
|
57
166
|
});
|
|
58
167
|
|
|
168
|
+
// Shared authority modules must run the full suite even when another changed file
|
|
169
|
+
// resolves a targeted test. Keep this signal separate from test candidates: an
|
|
170
|
+
// empty candidate list is not enough once a mapped consumer is also changed.
|
|
171
|
+
const FULL_SUITE_REQUIRED_FILES = new Set([
|
|
172
|
+
'lib/kernel/schema.js',
|
|
173
|
+
'lib/kernel/migrations.js',
|
|
174
|
+
'lib/kernel/sqlite-driver.js',
|
|
175
|
+
]);
|
|
176
|
+
|
|
177
|
+
// Directory-level mappings for source trees that own a dedicated suite. Without
|
|
178
|
+
// these a change under any of them resolves to zero tests and forces the full
|
|
179
|
+
// suite (see hasZeroResolvedTests in scripts/test.js).
|
|
180
|
+
const PREFIX_TEST_TARGETS = Object.freeze([
|
|
181
|
+
['validation/', ['test/validation/risk-manifest.test.js', 'test/validation/risk-manifest-generator.test.js']],
|
|
182
|
+
// `eval/corpus/` must come first: the corpus hash and manifest checks live in
|
|
183
|
+
// immutable-corpus, and eval-sets only reads `eval/commands/`.
|
|
184
|
+
['eval/corpus/', ['test/eval/immutable-corpus.test.js']],
|
|
185
|
+
['eval/', ['test/eval/eval-sets.test.js']],
|
|
186
|
+
['rules/', ['test/rules-sync.test.js', 'test/skill-dispatch-parity.test.js']],
|
|
187
|
+
['plugin/', ['test/activation/plugin-scaffold.test.js', 'test/plugin-catalog.test.js', 'test/release-readiness.test.js']],
|
|
188
|
+
]);
|
|
189
|
+
|
|
59
190
|
// Skill sources (canonical `skills/**` and the committed `.agents/skills/**` mirror)
|
|
60
191
|
// map to the fast skill suite. This is what keeps a skills-only PR on the targeted
|
|
61
192
|
// lane instead of the full ~1500-test suite. `skills-sync-drift` guards the mirror.
|
|
@@ -176,7 +307,9 @@ function getChangedFiles(execFileSync, options = {}) {
|
|
|
176
307
|
* @returns {string[]} Array of test file paths (e.g. ['test/foo.test.js'])
|
|
177
308
|
*/
|
|
178
309
|
function getAffectedTestFiles(projectRoot, execFileSync, fs = defaultFs, options = {}) {
|
|
179
|
-
const changedFiles =
|
|
310
|
+
const changedFiles = Array.isArray(options.changedFiles)
|
|
311
|
+
? options.changedFiles
|
|
312
|
+
: getChangedFiles(execFileSync, options);
|
|
180
313
|
const testFiles = new Set();
|
|
181
314
|
for (const file of changedFiles) {
|
|
182
315
|
for (const candidate of getTestCandidatesForChangedFile(file)) {
|
|
@@ -215,10 +348,24 @@ function getTestCandidatesForChangedFile(file) {
|
|
|
215
348
|
return [...SKILL_TEST_TARGETS];
|
|
216
349
|
}
|
|
217
350
|
|
|
351
|
+
// Maintainer-only contributor skills. They document the AGENTS.md product /
|
|
352
|
+
// maintainer split, so the docs-bleed gate is the suite that can catch a
|
|
353
|
+
// contradiction between them.
|
|
354
|
+
if (file.startsWith('.forge/contributor-skills/')) {
|
|
355
|
+
return ['test/structural/agents-docs-bleed.test.js'];
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
for (const [prefix, targets] of PREFIX_TEST_TARGETS) {
|
|
359
|
+
if (file.startsWith(prefix)) {
|
|
360
|
+
return [...targets];
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
218
364
|
if (file === 'README.md'
|
|
219
365
|
|| file === 'QUICKSTART.md'
|
|
220
366
|
|| file === 'CHANGELOG.md'
|
|
221
367
|
|| file === 'AGENTS.md'
|
|
368
|
+
|| file === 'CODING_STANDARDS.md'
|
|
222
369
|
|| file === 'DEVELOPMENT.md'
|
|
223
370
|
|| file === 'docs/INDEX.md'
|
|
224
371
|
|| file === 'docs/PROJECT_DESIGN.md'
|
|
@@ -226,7 +373,9 @@ function getTestCandidatesForChangedFile(file) {
|
|
|
226
373
|
|| file.startsWith('docs/guides/')
|
|
227
374
|
|| file.startsWith('docs/reference/')
|
|
228
375
|
|| file.startsWith('docs/work/')) {
|
|
229
|
-
return
|
|
376
|
+
return file === 'AGENTS.md'
|
|
377
|
+
? ['test/docs-consistency.test.js', 'test/structural/agents-docs-bleed.test.js']
|
|
378
|
+
: ['test/docs-consistency.test.js'];
|
|
230
379
|
}
|
|
231
380
|
|
|
232
381
|
if (file.startsWith('lib/') && file.endsWith('.js')) {
|
|
@@ -270,10 +419,15 @@ function getTestCandidatesForChangedFile(file) {
|
|
|
270
419
|
return [];
|
|
271
420
|
}
|
|
272
421
|
|
|
422
|
+
function isFullSuiteRequiredFile(file) {
|
|
423
|
+
return FULL_SUITE_REQUIRED_FILES.has(file);
|
|
424
|
+
}
|
|
425
|
+
|
|
273
426
|
module.exports = {
|
|
274
427
|
getChangedFiles,
|
|
275
428
|
getAffectedTestFiles,
|
|
276
429
|
getTestCandidatesForChangedFile,
|
|
430
|
+
isFullSuiteRequiredFile,
|
|
277
431
|
name: 'test',
|
|
278
432
|
description: 'Run tests with smart defaults (timeout, affected-only)',
|
|
279
433
|
usage: 'forge test [--affected]',
|
|
@@ -318,10 +472,15 @@ module.exports = {
|
|
|
318
472
|
|
|
319
473
|
// 4. --affected flag: find changed test files
|
|
320
474
|
if (flags['--affected'] || flags.affected) {
|
|
321
|
-
const
|
|
475
|
+
const diffOptions = {
|
|
322
476
|
sinceUpstream: flags.sinceUpstream || flags['--since-upstream'],
|
|
477
|
+
};
|
|
478
|
+
const changedFiles = getChangedFiles(execFileSync, diffOptions);
|
|
479
|
+
const affectedTests = getAffectedTestFiles(projectRoot, execFileSync, fs, {
|
|
480
|
+
...diffOptions,
|
|
481
|
+
changedFiles,
|
|
323
482
|
});
|
|
324
|
-
if (affectedTests.length > 0) {
|
|
483
|
+
if (!changedFiles.some(isFullSuiteRequiredFile) && affectedTests.length > 0) {
|
|
325
484
|
testArgs = ['run', 'test', ...affectedTests];
|
|
326
485
|
}
|
|
327
486
|
// If no affected tests found, fall back to running all tests
|