@rsaglobaltech/specgate 0.27.6 → 0.27.9
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/CHANGELOG.md +41 -0
- package/dist/packages/core/src/domain/DraftChecklist.js +58 -14
- package/dist/packages/core/src/domain/DraftChecklist.js.map +1 -1
- package/dist/packages/core/src/domain/HarnessReport.js +46 -10
- package/dist/packages/core/src/domain/HarnessReport.js.map +1 -1
- package/dist/packages/core/src/domain/HarnessRun.js.map +1 -1
- package/dist/packages/core/src/infrastructure/HarnessConfigFile.js.map +1 -1
- package/dist/scripts/cli/commands/config/ConfigSetCommand.js +23 -4
- package/dist/scripts/cli/commands/config/ConfigSetCommand.js.map +1 -1
- package/dist/scripts/cli/commands/harness/RunCommand.js +220 -25
- package/dist/scripts/cli/commands/harness/RunCommand.js.map +1 -1
- package/dist/scripts/cli/commands/spec/DraftCommand.js +21 -0
- package/dist/scripts/cli/commands/spec/DraftCommand.js.map +1 -1
- package/dist/scripts/cli/commands/spec/StatusCommand.js +35 -6
- package/dist/scripts/cli/commands/spec/StatusCommand.js.map +1 -1
- package/dist/scripts/draft/brief-setup.js +23 -0
- package/dist/scripts/draft/brief-setup.js.map +1 -1
- package/dist/scripts/harness/report.js +7 -1
- package/dist/scripts/harness/report.js.map +1 -1
- package/dist/scripts/lib/agent-permission.js +50 -0
- package/dist/scripts/lib/agent-permission.js.map +1 -0
- package/dist/scripts/lib/harness-environment.js +118 -0
- package/dist/scripts/lib/harness-environment.js.map +1 -0
- package/dist/scripts/lib/live-model.js +28 -2
- package/dist/scripts/lib/live-model.js.map +1 -1
- package/dist/scripts/lib/live-page.js +10 -4
- package/dist/scripts/lib/live-page.js.map +1 -1
- package/dist/scripts/lib/quality-presets.js +3 -1
- package/dist/scripts/lib/quality-presets.js.map +1 -1
- package/dist/scripts/lib/quality-run.js +58 -1
- package/dist/scripts/lib/quality-run.js.map +1 -1
- package/dist/scripts/lib/shell-command.js +38 -1
- package/dist/scripts/lib/shell-command.js.map +1 -1
- package/dist/scripts/lib/studio-app-page.js +5 -2
- package/dist/scripts/lib/studio-app-page.js.map +1 -1
- package/dist/scripts/lib/studio-app.js +44 -7
- package/dist/scripts/lib/studio-app.js.map +1 -1
- package/dist/scripts/lib/studio-route.js +34 -8
- package/dist/scripts/lib/studio-route.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfigSetCommand.js","sourceRoot":"","sources":["../../../../../scripts/cli/commands/config/ConfigSetCommand.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"ConfigSetCommand.js","sourceRoot":"","sources":["../../../../../scripts/cli/commands/config/ConfigSetCommand.ts"],"names":[],"mappings":"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"}
|
|
@@ -58,6 +58,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
58
58
|
})();
|
|
59
59
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
60
60
|
exports.RunCommand = exports.RUNS_DIR = void 0;
|
|
61
|
+
exports.promptInFlight = promptInFlight;
|
|
61
62
|
exports.setJsonMode = setJsonMode;
|
|
62
63
|
exports.error = error;
|
|
63
64
|
exports.parseArgs = parseArgs;
|
|
@@ -78,6 +79,7 @@ const node_child_process_1 = require("node:child_process");
|
|
|
78
79
|
const command_1 = require("../../../lib/command");
|
|
79
80
|
const project_root_1 = require("../../../lib/project-root");
|
|
80
81
|
const prompt_1 = require("../../../harness/prompt");
|
|
82
|
+
const agent_permission_1 = require("../../../lib/agent-permission");
|
|
81
83
|
const init_1 = require("../../../harness/init");
|
|
82
84
|
const in_flight_1 = require("../../../lib/in-flight");
|
|
83
85
|
const harness_events_1 = require("../../../lib/harness-events");
|
|
@@ -85,6 +87,7 @@ const clarification_1 = require("../../../lib/clarification");
|
|
|
85
87
|
const screen_requirements_1 = require("../../../lib/screen-requirements");
|
|
86
88
|
const harness_guard_1 = require("../../../lib/harness-guard");
|
|
87
89
|
const quality_run_1 = require("../../../lib/quality-run");
|
|
90
|
+
const harness_environment_1 = require("../../../lib/harness-environment");
|
|
88
91
|
const HarnessEvents_1 = require("../../../../packages/core/src/domain/HarnessEvents");
|
|
89
92
|
/**
|
|
90
93
|
* `read` against the project as it will be after its open changes are
|
|
@@ -101,6 +104,17 @@ function judged(dir, read) {
|
|
|
101
104
|
view.cleanup();
|
|
102
105
|
}
|
|
103
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* A prompt built from the project as the harness judges it (REQ-268).
|
|
109
|
+
*
|
|
110
|
+
* The implementer's prompt was; the reviewer's and the adversary's read the
|
|
111
|
+
* worktree as written, where a reviewed draft's requirement has no text, and
|
|
112
|
+
* the reviewer was told to stop and report that REQ-002 did not exist
|
|
113
|
+
* (truck-flote, S53).
|
|
114
|
+
*/
|
|
115
|
+
function promptInFlight(req, dir, opts = {}) {
|
|
116
|
+
return judged(dir, (judgedDir) => (0, prompt_1.buildPrompt)(req, judgedDir, opts));
|
|
117
|
+
}
|
|
104
118
|
const GherkinQuality_1 = require("../../../../packages/core/src/domain/GherkinQuality");
|
|
105
119
|
const HarnessConfigFile_1 = require("../../../../packages/core/src/infrastructure/HarnessConfigFile");
|
|
106
120
|
const agent_1 = require("../../../lib/agent");
|
|
@@ -492,6 +506,41 @@ function baselineProblem(projectDir, running) {
|
|
|
492
506
|
"test. Write those tests or move those requirements back to Draft (`specgate check` shows " +
|
|
493
507
|
"it whole), or pass --skip-baseline.");
|
|
494
508
|
}
|
|
509
|
+
/** "0.4 s", "12 s", "3m 05s": how long a gate step took. */
|
|
510
|
+
function stepTime(ms) {
|
|
511
|
+
if (ms < 10_000)
|
|
512
|
+
return `${(ms / 1000).toFixed(1)} s`;
|
|
513
|
+
const secs = Math.round(ms / 1000);
|
|
514
|
+
if (secs < 60)
|
|
515
|
+
return `${secs} s`;
|
|
516
|
+
return `${Math.floor(secs / 60)}m ${String(secs % 60).padStart(2, "0")}s`;
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* REQ-273: a gate step is an event when it starts and when it ends, and a line
|
|
520
|
+
* of the run's output each time. A check that hung for 15 minutes printed
|
|
521
|
+
* nothing and showed nothing in `/live` until it ended (truck-flote, S54).
|
|
522
|
+
*/
|
|
523
|
+
function gateReporter(eventsFile, requirement, attempt, prefix = "") {
|
|
524
|
+
return (stage, result, extra) => {
|
|
525
|
+
const named = `${prefix}${stage}`;
|
|
526
|
+
// The line names the test step, not its command: what the command prints
|
|
527
|
+
// stays off a green run's output unless `--verbose` asks for it.
|
|
528
|
+
const shown = named.replace(/^((?:post-merge )?test command): [\s\S]*$/, "$1");
|
|
529
|
+
if (result === "start") {
|
|
530
|
+
(0, harness_events_1.appendEvent)(eventsFile, { type: "gate-start", stage: named, ...(extra || {}) }, requirement, attempt);
|
|
531
|
+
const where = extra && extra.image
|
|
532
|
+
? ` (${extra.runner} ${extra.image})`
|
|
533
|
+
: extra && extra.runner
|
|
534
|
+
? ` (${extra.runner})`
|
|
535
|
+
: "";
|
|
536
|
+
info(`${requirement}: started ${shown}${where}`);
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
(0, harness_events_1.appendEvent)(eventsFile, { type: "gate", stage: named, result, ...(extra || {}) }, requirement, attempt);
|
|
540
|
+
const ms = extra && typeof extra.ms === "number" ? ` in ${stepTime(extra.ms)}` : "";
|
|
541
|
+
info(`${requirement}: ${result === "pass" ? "passed" : "failed"} ${shown}${ms}`);
|
|
542
|
+
};
|
|
543
|
+
}
|
|
495
544
|
function runGate(worktreeDir, testCmd, timeoutMs, req = {}, settings = {}, report = () => { }) {
|
|
496
545
|
// `--delivering`: the requirement is still Draft while its gate runs, and a
|
|
497
546
|
// Draft row owes no files. Without it the gate would skip the one
|
|
@@ -500,6 +549,8 @@ function runGate(worktreeDir, testCmd, timeoutMs, req = {}, settings = {}, repor
|
|
|
500
549
|
// the gate judges the attempt, not the matrix as it was before it.
|
|
501
550
|
(0, MatrixCommand_1.refreshDerivedMatrix)(worktreeDir);
|
|
502
551
|
let passHint = "";
|
|
552
|
+
report("validate --strict", "start");
|
|
553
|
+
const validateStart = Date.now();
|
|
503
554
|
const validate = judged(worktreeDir, (dir, toProject) => {
|
|
504
555
|
const validateArgs = [VALIDATE_SCRIPT, dir, "--strict"];
|
|
505
556
|
if (req.requirement)
|
|
@@ -512,7 +563,9 @@ function runGate(worktreeDir, testCmd, timeoutMs, req = {}, settings = {}, repor
|
|
|
512
563
|
// The agent is told about its worktree, not about the copy it was judged in.
|
|
513
564
|
return { ...run, stdout: toProject(run.stdout || ""), stderr: toProject(run.stderr || "") };
|
|
514
565
|
});
|
|
515
|
-
report("validate --strict", validate.status === 0 ? "pass" : "fail"
|
|
566
|
+
report("validate --strict", validate.status === 0 ? "pass" : "fail", {
|
|
567
|
+
ms: Date.now() - validateStart,
|
|
568
|
+
});
|
|
516
569
|
if (validate.status !== 0) {
|
|
517
570
|
return {
|
|
518
571
|
ok: false,
|
|
@@ -524,8 +577,10 @@ function runGate(worktreeDir, testCmd, timeoutMs, req = {}, settings = {}, repor
|
|
|
524
577
|
// REQ-199: a requirement used on a screen needs an end-to-end test that
|
|
525
578
|
// names it, when the project says where those live.
|
|
526
579
|
if (settings.e2eDir && req.requirement && (0, screen_requirements_1.onScreen)(worktreeDir, req.requirement)) {
|
|
580
|
+
report("e2e test", "start");
|
|
581
|
+
const e2eStart = Date.now();
|
|
527
582
|
const named = (0, screen_requirements_1.e2eTestsNaming)(worktreeDir, settings.e2eDir, req.requirement);
|
|
528
|
-
report("e2e test", named.length ? "pass" : "fail");
|
|
583
|
+
report("e2e test", named.length ? "pass" : "fail", { ms: Date.now() - e2eStart });
|
|
529
584
|
if (named.length === 0) {
|
|
530
585
|
return {
|
|
531
586
|
ok: false,
|
|
@@ -540,6 +595,8 @@ function runGate(worktreeDir, testCmd, timeoutMs, req = {}, settings = {}, repor
|
|
|
540
595
|
// and its failure goes back to the agent like any other gate failure.
|
|
541
596
|
const archCmd = String(settings.archCmd || "").trim();
|
|
542
597
|
if (archCmd) {
|
|
598
|
+
report(`architecture check: ${archCmd}`, "start");
|
|
599
|
+
const archStart = Date.now();
|
|
543
600
|
// REQ-231: with arch_sarif, only a violation this attempt adds fails it.
|
|
544
601
|
const arch = settings.archSarif
|
|
545
602
|
? (0, quality_run_1.archRun)(worktreeDir, archCmd, settings.archSarif, "", req.requirement || "")
|
|
@@ -550,7 +607,9 @@ function runGate(worktreeDir, testCmd, timeoutMs, req = {}, settings = {}, repor
|
|
|
550
607
|
timeout: timeoutMs,
|
|
551
608
|
maxBuffer: SUBPROCESS_MAX_BUFFER,
|
|
552
609
|
});
|
|
553
|
-
report(`architecture check: ${archCmd}`, arch.status === 0 ? "pass" : "fail"
|
|
610
|
+
report(`architecture check: ${archCmd}`, arch.status === 0 ? "pass" : "fail", {
|
|
611
|
+
ms: Date.now() - archStart,
|
|
612
|
+
});
|
|
554
613
|
if (arch.status !== 0) {
|
|
555
614
|
return {
|
|
556
615
|
ok: false,
|
|
@@ -587,6 +646,8 @@ function runGate(worktreeDir, testCmd, timeoutMs, req = {}, settings = {}, repor
|
|
|
587
646
|
// Stderr rather than inherited stdout, because `--format json` promises one
|
|
588
647
|
// parseable document on stdout. Letting the test command write there is
|
|
589
648
|
// precisely H18: prose and JSON sharing a stream.
|
|
649
|
+
report(`test command: ${(0, HarnessEvents_1.maskCommand)(resolved)}`, "start");
|
|
650
|
+
const testStart = Date.now();
|
|
590
651
|
const test = (0, node_child_process_1.spawnSync)((0, shell_command_1.forShell)(resolved), {
|
|
591
652
|
shell: true,
|
|
592
653
|
cwd: worktreeDir,
|
|
@@ -595,7 +656,21 @@ function runGate(worktreeDir, testCmd, timeoutMs, req = {}, settings = {}, repor
|
|
|
595
656
|
maxBuffer: SUBPROCESS_MAX_BUFFER,
|
|
596
657
|
stdio: settings.verbose ? ["ignore", 2, 2] : undefined,
|
|
597
658
|
});
|
|
598
|
-
report(`test command: ${(0, HarnessEvents_1.maskCommand)(resolved)}`, test.status === 0 ? "pass" : "fail",
|
|
659
|
+
report(`test command: ${(0, HarnessEvents_1.maskCommand)(resolved)}`, test.status === 0 ? "pass" : "fail", {
|
|
660
|
+
...(0, HarnessEvents_1.testCounts)(`${test.stdout || ""}${test.stderr || ""}`),
|
|
661
|
+
ms: Date.now() - testStart,
|
|
662
|
+
});
|
|
663
|
+
// REQ-279: a test command that could not reach Docker failed for the
|
|
664
|
+
// machine, not the code; the person starts Docker, the agent cannot.
|
|
665
|
+
const missing = test.status !== 0 ? (0, harness_environment_1.environmentFailure)(`${test.stdout || ""}${test.stderr || ""}`) : null;
|
|
666
|
+
if (missing) {
|
|
667
|
+
return {
|
|
668
|
+
ok: false,
|
|
669
|
+
stage: `test command: ${resolved}`,
|
|
670
|
+
output: `${test.stdout || ""}${test.stderr || ""}`,
|
|
671
|
+
couldNotRun: missing,
|
|
672
|
+
};
|
|
673
|
+
}
|
|
599
674
|
if (test.status !== 0) {
|
|
600
675
|
// Name the command. A gate that silently does the wrong thing — running
|
|
601
676
|
// the whole suite because a filter did not apply, say — produces a
|
|
@@ -642,8 +717,14 @@ function runGate(worktreeDir, testCmd, timeoutMs, req = {}, settings = {}, repor
|
|
|
642
717
|
const ctx = (0, quality_run_1.gateContext)(worktreeDir, req.requirement || "", "");
|
|
643
718
|
const outcomes = [];
|
|
644
719
|
for (const stage of ["attempt", "end"]) {
|
|
645
|
-
|
|
646
|
-
|
|
720
|
+
// REQ-273: each check is reported as it starts and as it ends — the
|
|
721
|
+
// stage used to run every check before the first was reported.
|
|
722
|
+
const asItRuns = (check, run) => {
|
|
723
|
+
report(`quality: ${check.name}`, "start", {
|
|
724
|
+
runner: check.runner,
|
|
725
|
+
...(check.image ? { image: check.image } : {}),
|
|
726
|
+
});
|
|
727
|
+
const o = run();
|
|
647
728
|
// REQ-229: the step says what was measured, so /live shows it as it happens.
|
|
648
729
|
const said = o.skipped
|
|
649
730
|
? " (not run)"
|
|
@@ -657,6 +738,20 @@ function runGate(worktreeDir, testCmd, timeoutMs, req = {}, settings = {}, repor
|
|
|
657
738
|
...(o.skipped ? { skipped: o.skipped } : {}),
|
|
658
739
|
...(o.measure ? { value: o.measure.value } : {}),
|
|
659
740
|
});
|
|
741
|
+
return o;
|
|
742
|
+
};
|
|
743
|
+
for (const o of (0, quality_run_1.runStages)(worktreeDir, quality, [stage], ctx, asItRuns, agentChecks(worktreeDir, req, settings, timeoutMs, testCmd))) {
|
|
744
|
+
outcomes.push(o);
|
|
745
|
+
// REQ-267: a check that could not run is not the agent's to fix.
|
|
746
|
+
if (o.gate && !o.ok && !o.skipped && o.couldNotRun) {
|
|
747
|
+
return {
|
|
748
|
+
ok: false,
|
|
749
|
+
stage: `quality check: ${o.name}`,
|
|
750
|
+
output: o.output,
|
|
751
|
+
couldNotRun: `the quality check ${o.name} could not run: it ${o.couldNotRun}`,
|
|
752
|
+
checks: outcomes,
|
|
753
|
+
};
|
|
754
|
+
}
|
|
660
755
|
if (o.gate && !o.ok && !o.skipped) {
|
|
661
756
|
return {
|
|
662
757
|
ok: false,
|
|
@@ -790,7 +885,7 @@ function agentChecks(worktreeDir, req, settings, timeoutMs, testCmd) {
|
|
|
790
885
|
return settings.agent || "";
|
|
791
886
|
}
|
|
792
887
|
};
|
|
793
|
-
const promptFor = (instruction) => (
|
|
888
|
+
const promptFor = (instruction) => promptInFlight(req, worktreeDir, {
|
|
794
889
|
promptPrefix: settings.promptPrefix,
|
|
795
890
|
hint: instruction,
|
|
796
891
|
withPrecedents: settings.promptPrecedents === true,
|
|
@@ -1189,6 +1284,7 @@ function attemptRequirement(req, ctx) {
|
|
|
1189
1284
|
totalMs: Date.now() - attemptStart,
|
|
1190
1285
|
profiles: [...profilesUsed],
|
|
1191
1286
|
});
|
|
1287
|
+
ledgerAttempts(attemptLog);
|
|
1192
1288
|
};
|
|
1193
1289
|
// An attempt is a sequence of agent invocations, all bound to this one
|
|
1194
1290
|
// requirement. Attempt 1 is a single implementing step; a retry may run an
|
|
@@ -1330,17 +1426,21 @@ function attemptRequirement(req, ctx) {
|
|
|
1330
1426
|
}
|
|
1331
1427
|
// Before the gate: an agent that wrote nothing cannot have implemented
|
|
1332
1428
|
// anything, and the gate would approve it. See `producedNothing` (H19).
|
|
1429
|
+
// REQ-264: the same command writes nothing the next time either — three
|
|
1430
|
+
// attempts and 7 min 40 s said so in truck-flote (S50) — so it ends here.
|
|
1333
1431
|
if (producedNothing(worktreeDir)) {
|
|
1334
1432
|
previousFailure =
|
|
1335
1433
|
`You produced no files. Nothing in the worktree changed, so there is ` +
|
|
1336
1434
|
`nothing implementing ${req.requirement}.\n\n` +
|
|
1337
1435
|
`If you could not write, that is the problem to report — a non-interactive ` +
|
|
1338
1436
|
`agent without tool permissions reads the prompt and cannot act on it. ` +
|
|
1339
|
-
`See docs/
|
|
1437
|
+
`See docs/agent-setup.md, § Tool allowances.`;
|
|
1340
1438
|
warn(`${req.requirement}: the agent produced no files — nothing to gate`);
|
|
1341
|
-
warn(` fix:
|
|
1439
|
+
warn(` fix: give the agent write permission and the tools it needs (docs/agent-setup.md, § Tool allowances)`);
|
|
1440
|
+
warn(` not retrying: the same command would write nothing again`);
|
|
1342
1441
|
record("no-op");
|
|
1343
|
-
|
|
1442
|
+
stopRetrying = true;
|
|
1443
|
+
break;
|
|
1344
1444
|
}
|
|
1345
1445
|
// Before the gate: a scenario the agent has just loosened cannot fail, so a
|
|
1346
1446
|
// green gate proves nothing. See `checkWriteScopeInWorktree`.
|
|
@@ -1365,7 +1465,21 @@ function attemptRequirement(req, ctx) {
|
|
|
1365
1465
|
}
|
|
1366
1466
|
// `verbose` is a run flag, not a project setting, so it travels on the
|
|
1367
1467
|
// context rather than in `harness.config.yaml`.
|
|
1368
|
-
const gate = runGate(worktreeDir, settings.testCmd, timeoutMs, req, { ...settings, verbose: ctx.verbose }, (
|
|
1468
|
+
const gate = runGate(worktreeDir, settings.testCmd, timeoutMs, req, { ...settings, verbose: ctx.verbose }, gateReporter(eventsFile, req.requirement, attempt));
|
|
1469
|
+
if (!gate.ok && gate.couldNotRun) {
|
|
1470
|
+
// REQ-267: nothing in the code can fix the tool's own setup, and another
|
|
1471
|
+
// attempt would meet it again (truck-flote S52: 15 minutes each).
|
|
1472
|
+
previousFailure = `Gate stopped at: ${gate.stage}\n\n${gate.couldNotRun}\n\n${gate.output}`;
|
|
1473
|
+
warn(`${req.requirement}: ${gate.couldNotRun}`);
|
|
1474
|
+
for (const line of gate.output.trim().split("\n").slice(-5))
|
|
1475
|
+
warn(` ${line}`);
|
|
1476
|
+
warn(gate.stage.startsWith("quality check")
|
|
1477
|
+
? ` not retrying: fix the check (harness.config.yaml) and run it again`
|
|
1478
|
+
: ` not retrying: the agent cannot fix the machine`);
|
|
1479
|
+
record("check");
|
|
1480
|
+
stopRetrying = true;
|
|
1481
|
+
break;
|
|
1482
|
+
}
|
|
1369
1483
|
if (!gate.ok) {
|
|
1370
1484
|
previousFailure =
|
|
1371
1485
|
`Gate failed at: ${gate.stage}\n\n` + (gate.hint ? `⚠ ${gate.hint}\n\n` : "") + gate.output;
|
|
@@ -1444,7 +1558,7 @@ function attemptRequirement(req, ctx) {
|
|
|
1444
1558
|
alreadyProbed: probe.ran,
|
|
1445
1559
|
})) {
|
|
1446
1560
|
info(`${req.requirement}: probing with '${settings.adversaryProfile}'`);
|
|
1447
|
-
probe = runAdversarialProbe(worktreeDir, req, settings, timeoutMs, (instruction) => (
|
|
1561
|
+
probe = runAdversarialProbe(worktreeDir, req, settings, timeoutMs, (instruction) => promptInFlight(req, worktreeDir, {
|
|
1448
1562
|
promptPrefix: settings.promptPrefix,
|
|
1449
1563
|
hint: instruction,
|
|
1450
1564
|
withPrecedents: settings.promptPrecedents === true,
|
|
@@ -1540,6 +1654,28 @@ function recordQuestions(projectDir, results) {
|
|
|
1540
1654
|
}
|
|
1541
1655
|
/** Where a run's record lands. Local to the machine — see `writeRunRecord`. */
|
|
1542
1656
|
exports.RUNS_DIR = path.join(".harness", "runs");
|
|
1657
|
+
/**
|
|
1658
|
+
* The run's record, written as it goes (REQ-269).
|
|
1659
|
+
*
|
|
1660
|
+
* It used to be written once, at the end, so a run stopped by hand — the
|
|
1661
|
+
* 27 minutes truck-flote spent on semgrep hanging (S52) — left no trace, and
|
|
1662
|
+
* `harness report` priced the requirement without them. Now it is written when
|
|
1663
|
+
* each requirement starts and each attempt ends; a killed run leaves a record
|
|
1664
|
+
* whose `running` says where it stopped.
|
|
1665
|
+
*/
|
|
1666
|
+
let ledger = null;
|
|
1667
|
+
function ledgerWrite() {
|
|
1668
|
+
if (!ledger)
|
|
1669
|
+
return;
|
|
1670
|
+
ledger.record.updatedAt = new Date().toISOString();
|
|
1671
|
+
writeRunRecord(ledger.projectDir, ledger.record);
|
|
1672
|
+
}
|
|
1673
|
+
function ledgerAttempts(attemptLog) {
|
|
1674
|
+
if (!ledger || !ledger.record.running)
|
|
1675
|
+
return;
|
|
1676
|
+
ledger.record.running.attemptLog = [...attemptLog];
|
|
1677
|
+
ledgerWrite();
|
|
1678
|
+
}
|
|
1543
1679
|
/**
|
|
1544
1680
|
* Write what a run did, so the next question about it has an answer.
|
|
1545
1681
|
*
|
|
@@ -1922,6 +2058,23 @@ function gitIdentityProblem(projectDir) {
|
|
|
1922
2058
|
`Fix: git config user.name "Your Name" && git config user.email "you@example.com"`);
|
|
1923
2059
|
}
|
|
1924
2060
|
function processRequirement(req, ctx) {
|
|
2061
|
+
if (ledger) {
|
|
2062
|
+
ledger.record.running = {
|
|
2063
|
+
requirement: req.requirement,
|
|
2064
|
+
startedAt: new Date().toISOString(),
|
|
2065
|
+
attemptLog: [],
|
|
2066
|
+
};
|
|
2067
|
+
ledgerWrite();
|
|
2068
|
+
}
|
|
2069
|
+
const result = processOneRequirement(req, ctx);
|
|
2070
|
+
if (ledger) {
|
|
2071
|
+
ledger.record.results.push(result);
|
|
2072
|
+
delete ledger.record.running;
|
|
2073
|
+
ledgerWrite();
|
|
2074
|
+
}
|
|
2075
|
+
return result;
|
|
2076
|
+
}
|
|
2077
|
+
function processOneRequirement(req, ctx) {
|
|
1925
2078
|
const { projectDir, baseRef, keepWorktrees, force } = ctx;
|
|
1926
2079
|
const branch = `harness/${req.requirement}`;
|
|
1927
2080
|
// D1: which profile this requirement matches, resolved once. The bounded
|
|
@@ -2049,8 +2202,13 @@ function processRequirement(req, ctx) {
|
|
|
2049
2202
|
try {
|
|
2050
2203
|
const setupCmd = String((ctx.settings && ctx.settings.setupCmd) || "").trim();
|
|
2051
2204
|
if (setupCmd && !survivor) {
|
|
2052
|
-
|
|
2205
|
+
const report = gateReporter((0, harness_events_1.eventsFileFor)(ctx.projectDir || dir, req.requirement), req.requirement, undefined);
|
|
2206
|
+
report(`setup: ${setupCmd}`, "start");
|
|
2207
|
+
const setupStart = Date.now();
|
|
2053
2208
|
const setup = (0, node_child_process_1.spawnSync)((0, shell_command_1.forShell)(setupCmd), { cwd: dir, shell: true, encoding: "utf8" });
|
|
2209
|
+
report(`setup: ${setupCmd}`, setup.status === 0 ? "pass" : "fail", {
|
|
2210
|
+
ms: Date.now() - setupStart,
|
|
2211
|
+
});
|
|
2054
2212
|
if (setup.status !== 0) {
|
|
2055
2213
|
return {
|
|
2056
2214
|
requirement: req.requirement,
|
|
@@ -2382,7 +2540,7 @@ async function runChain(pending, ctx, hintByReq) {
|
|
|
2382
2540
|
(0, harness_events_1.appendEvent)(eventsFile, { type: "merge", into: "HEAD", branch }, req.requirement, attempt);
|
|
2383
2541
|
const gate = runGate(projectDir, ctx.settings.testCmd, ctx.timeoutMs, req,
|
|
2384
2542
|
// The checks judged the branch already; the post-merge gate is the suite's.
|
|
2385
|
-
{ ...ctx.settings, quality: undefined }, (
|
|
2543
|
+
{ ...ctx.settings, quality: undefined }, gateReporter(eventsFile, req.requirement, attempt, "post-merge "));
|
|
2386
2544
|
if (!gate.ok) {
|
|
2387
2545
|
git(projectDir, ["reset", "-q", "--hard", "HEAD~1"]);
|
|
2388
2546
|
Object.assign(result, {
|
|
@@ -2724,6 +2882,24 @@ class RunCommand extends command_1.BaseCommand {
|
|
|
2724
2882
|
}
|
|
2725
2883
|
}
|
|
2726
2884
|
}
|
|
2885
|
+
// REQ-264: Claude Code in print mode writes nothing without permission.
|
|
2886
|
+
let permitted = false;
|
|
2887
|
+
const permit = (command) => {
|
|
2888
|
+
const p = (0, agent_permission_1.withAgentPermission)(command);
|
|
2889
|
+
permitted = permitted || p.added;
|
|
2890
|
+
return p.command;
|
|
2891
|
+
};
|
|
2892
|
+
if (settings.agent)
|
|
2893
|
+
settings.agent = permit(settings.agent);
|
|
2894
|
+
for (const name of Object.keys(settings.profileAgents || {})) {
|
|
2895
|
+
settings.profileAgents[name] = permit(settings.profileAgents[name]);
|
|
2896
|
+
}
|
|
2897
|
+
if (permitted) {
|
|
2898
|
+
info("Claude Code runs with --permission-mode acceptEdits: in print mode it cannot write " +
|
|
2899
|
+
"without it, and the harness's worktree confines what it writes.");
|
|
2900
|
+
}
|
|
2901
|
+
// REQ-271: an agent given on the command line is forgotten after the run.
|
|
2902
|
+
const unkept = Boolean(args.agent) && !(fileConfig && fileConfig.agent);
|
|
2727
2903
|
// A ladder of per-attempt profiles configures agents just as much as a
|
|
2728
2904
|
// single `agent:` does, so a project that declares one is configured.
|
|
2729
2905
|
const hasAgent = Boolean(settings.agent) || settings.attemptProfiles.length > 0;
|
|
@@ -2791,6 +2967,12 @@ class RunCommand extends command_1.BaseCommand {
|
|
|
2791
2967
|
const identity = gitIdentityProblem(projectDir);
|
|
2792
2968
|
if (identity)
|
|
2793
2969
|
throw new Error(identity);
|
|
2970
|
+
// REQ-279: a stopped Docker fails every attempt in the tests or the
|
|
2971
|
+
// checks, and the agent cannot start it (truck-flote S61: three attempts).
|
|
2972
|
+
const needsDocker = (0, harness_environment_1.dockerNeed)(projectDir, settings.quality || null);
|
|
2973
|
+
const noDocker = needsDocker ? (0, quality_run_1.dockerWhyNot)() : null;
|
|
2974
|
+
if (noDocker)
|
|
2975
|
+
throw new Error(`${noDocker} This project needs it: ${needsDocker}.`);
|
|
2794
2976
|
if (args.chain) {
|
|
2795
2977
|
if (args.baseBranch) {
|
|
2796
2978
|
throw new Error("--chain merges into the branch you are on: check that branch out instead of --base-branch.");
|
|
@@ -2868,6 +3050,23 @@ class RunCommand extends command_1.BaseCommand {
|
|
|
2868
3050
|
(0, harness_events_1.writeQueue)(projectDir, pending.map((r) => r.requirement));
|
|
2869
3051
|
}
|
|
2870
3052
|
const startedAt = new Date().toISOString();
|
|
3053
|
+
const runRecordBase = {
|
|
3054
|
+
schemaVersion: 1,
|
|
3055
|
+
startedAt,
|
|
3056
|
+
baseRef,
|
|
3057
|
+
concurrency: settings.concurrency,
|
|
3058
|
+
maxAttempts: settings.maxAttempts,
|
|
3059
|
+
// Whether this run had a ceiling at all. Reading the ledger later,
|
|
3060
|
+
// a short run is a different thing depending on the answer.
|
|
3061
|
+
bounded: (0, RunBudget_1.hasBudget)({
|
|
3062
|
+
budgetSeconds: args.budgetSeconds,
|
|
3063
|
+
maxRequirements: args.maxRequirements,
|
|
3064
|
+
}),
|
|
3065
|
+
};
|
|
3066
|
+
if (!process.env.SPECGATE_HARNESS_WORKER) {
|
|
3067
|
+
ledger = { projectDir, record: { ...runRecordBase, finishedAt: "", results: [] } };
|
|
3068
|
+
ledgerWrite();
|
|
3069
|
+
}
|
|
2871
3070
|
const results = args.chain
|
|
2872
3071
|
? await runChain(pending, ctx, hintByReq)
|
|
2873
3072
|
: await runLevels(pending, ctx, {
|
|
@@ -2885,21 +3084,17 @@ class RunCommand extends command_1.BaseCommand {
|
|
|
2885
3084
|
const recordPath = process.env.SPECGATE_HARNESS_WORKER
|
|
2886
3085
|
? null
|
|
2887
3086
|
: writeRunRecord(projectDir, {
|
|
2888
|
-
|
|
2889
|
-
startedAt,
|
|
3087
|
+
...runRecordBase,
|
|
2890
3088
|
finishedAt: new Date().toISOString(),
|
|
2891
|
-
baseRef,
|
|
2892
|
-
concurrency: settings.concurrency,
|
|
2893
|
-
maxAttempts: settings.maxAttempts,
|
|
2894
|
-
// Whether this run had a ceiling at all. Reading the ledger later,
|
|
2895
|
-
// a short run is a different thing depending on the answer.
|
|
2896
|
-
bounded: (0, RunBudget_1.hasBudget)({
|
|
2897
|
-
budgetSeconds: args.budgetSeconds,
|
|
2898
|
-
maxRequirements: args.maxRequirements,
|
|
2899
|
-
}),
|
|
2900
3089
|
results,
|
|
2901
3090
|
});
|
|
3091
|
+
ledger = null;
|
|
2902
3092
|
printReport(results, args.format, recordPath ? path.relative(projectDir, recordPath) : undefined);
|
|
3093
|
+
// REQ-271: the studio's Build reads the agent from harness.config.yaml.
|
|
3094
|
+
if (unkept && !jsonMode && !process.env.SPECGATE_HARNESS_WORKER) {
|
|
3095
|
+
process.stdout.write(` ${ui.muted("Keep this agent for the next run and the studio's Build:")}\n` +
|
|
3096
|
+
` specgate config set agent "${args.agent.replace(/"/g, '\\"')}"\n\n`);
|
|
3097
|
+
}
|
|
2903
3098
|
// A blocked requirement was never attempted, so it is not a pass — but it
|
|
2904
3099
|
// is also not evidence that anything is broken beyond the failure that
|
|
2905
3100
|
// caused it. It still fails the run, because work was left undone.
|