@rsaglobaltech/specgate 0.24.0 → 0.26.0
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 +91 -42
- package/dist/packages/core/src/domain/AgentPrompt.js +12 -0
- package/dist/packages/core/src/domain/AgentPrompt.js.map +1 -1
- package/dist/packages/core/src/domain/Clarification.js +178 -0
- package/dist/packages/core/src/domain/Clarification.js.map +1 -0
- package/dist/packages/core/src/domain/DraftChecklist.js +16 -16
- package/dist/packages/core/src/domain/DraftChecklist.js.map +1 -1
- package/dist/packages/core/src/domain/HarnessConfig.js.map +1 -1
- package/dist/packages/core/src/domain/HarnessRun.js.map +1 -1
- package/dist/packages/core/src/domain/RequirementPlan.js +11 -0
- package/dist/packages/core/src/domain/RequirementPlan.js.map +1 -1
- package/dist/packages/core/src/infrastructure/ChangeWorkspace.js +15 -3
- package/dist/packages/core/src/infrastructure/ChangeWorkspace.js.map +1 -1
- package/dist/packages/core/src/infrastructure/HarnessConfigFile.js +5 -0
- package/dist/packages/core/src/infrastructure/HarnessConfigFile.js.map +1 -1
- package/dist/packages/core/src/infrastructure/IdsAcrossBranches.js +4 -1
- package/dist/packages/core/src/infrastructure/IdsAcrossBranches.js.map +1 -1
- package/dist/packages/core/test/unit/clarification.test.js +161 -0
- package/dist/packages/core/test/unit/clarification.test.js.map +1 -0
- package/dist/packages/core/test/unit/rework.test.js +62 -0
- package/dist/packages/core/test/unit/rework.test.js.map +1 -0
- package/dist/scripts/answer.js +10 -0
- package/dist/scripts/answer.js.map +1 -0
- package/dist/scripts/ask.js +10 -0
- package/dist/scripts/ask.js.map +1 -0
- package/dist/scripts/brief_add.js +10 -0
- package/dist/scripts/brief_add.js.map +1 -0
- package/dist/scripts/cli/commands/change/ChangeCommand.js +124 -1
- package/dist/scripts/cli/commands/change/ChangeCommand.js.map +1 -1
- package/dist/scripts/cli/commands/harness/RunCommand.js +135 -5
- package/dist/scripts/cli/commands/harness/RunCommand.js.map +1 -1
- package/dist/scripts/cli/commands/spec/ClarifyCommands.js +275 -0
- package/dist/scripts/cli/commands/spec/ClarifyCommands.js.map +1 -0
- package/dist/scripts/cli/commands/spec/DraftCommand.js +44 -4
- package/dist/scripts/cli/commands/spec/DraftCommand.js.map +1 -1
- package/dist/scripts/cli/commands/spec/StatusCommand.js +5 -1
- package/dist/scripts/cli/commands/spec/StatusCommand.js.map +1 -1
- package/dist/scripts/cli/commands/spec/StudioCommand.js +194 -1
- package/dist/scripts/cli/commands/spec/StudioCommand.js.map +1 -1
- package/dist/scripts/docs/record-journey.js +1 -0
- package/dist/scripts/docs/record-journey.js.map +1 -1
- package/dist/scripts/docs/record-terminal.js +18 -0
- package/dist/scripts/docs/record-terminal.js.map +1 -1
- package/dist/scripts/draft/drafting-guide.js +11 -1
- package/dist/scripts/draft/drafting-guide.js.map +1 -1
- package/dist/scripts/harness/prompt.js +2 -0
- package/dist/scripts/harness/prompt.js.map +1 -1
- package/dist/scripts/lib/brief-add.js +197 -0
- package/dist/scripts/lib/brief-add.js.map +1 -0
- package/dist/scripts/lib/change-impact.js +156 -0
- package/dist/scripts/lib/change-impact.js.map +1 -0
- package/dist/scripts/lib/change-review.js +145 -0
- package/dist/scripts/lib/change-review.js.map +1 -0
- package/dist/scripts/lib/clarification.js +319 -0
- package/dist/scripts/lib/clarification.js.map +1 -0
- package/dist/scripts/lib/harness-guard.js +18 -0
- package/dist/scripts/lib/harness-guard.js.map +1 -1
- package/dist/scripts/lib/in-flight.js +17 -11
- package/dist/scripts/lib/in-flight.js.map +1 -1
- package/dist/scripts/lib/live-detail.js +3 -2
- package/dist/scripts/lib/live-detail.js.map +1 -1
- package/dist/scripts/lib/live-model.js +35 -4
- package/dist/scripts/lib/live-model.js.map +1 -1
- package/dist/scripts/lib/live-page.js +80 -15
- package/dist/scripts/lib/live-page.js.map +1 -1
- package/dist/scripts/lib/screen-requirements.js +149 -0
- package/dist/scripts/lib/screen-requirements.js.map +1 -0
- package/dist/scripts/lib/studio-app-page.js +960 -0
- package/dist/scripts/lib/studio-app-page.js.map +1 -0
- package/dist/scripts/lib/studio-app.js +362 -0
- package/dist/scripts/lib/studio-app.js.map +1 -0
- package/dist/scripts/lib/studio-route.js +933 -0
- package/dist/scripts/lib/studio-route.js.map +1 -0
- package/dist/scripts/lib/surface.js +52 -0
- package/dist/scripts/lib/surface.js.map +1 -1
- package/dist/scripts/questions.js +10 -0
- package/dist/scripts/questions.js.map +1 -0
- package/package.json +1 -1
- package/templates/harness/harness.config.yaml.tpl +10 -1
|
@@ -81,6 +81,8 @@ const prompt_1 = require("../../../harness/prompt");
|
|
|
81
81
|
const init_1 = require("../../../harness/init");
|
|
82
82
|
const in_flight_1 = require("../../../lib/in-flight");
|
|
83
83
|
const harness_events_1 = require("../../../lib/harness-events");
|
|
84
|
+
const clarification_1 = require("../../../lib/clarification");
|
|
85
|
+
const screen_requirements_1 = require("../../../lib/screen-requirements");
|
|
84
86
|
const harness_guard_1 = require("../../../lib/harness-guard");
|
|
85
87
|
const HarnessEvents_1 = require("../../../../packages/core/src/domain/HarnessEvents");
|
|
86
88
|
/**
|
|
@@ -170,6 +172,8 @@ function usage() {
|
|
|
170
172
|
"Runs the plan → agent → verify → done loop for every pending requirement.\n\n" +
|
|
171
173
|
" --agent <cmd> Agent command; must contain the {prompt_file} placeholder.\n" +
|
|
172
174
|
' e.g. --agent "claude -p < {prompt_file}"\n' +
|
|
175
|
+
" --profile <name> Run this profile's agent from .harness/profiles.yaml instead\n" +
|
|
176
|
+
" of the configured one, for this run only.\n" +
|
|
173
177
|
" --test-cmd <cmd> Project test command run as part of the gate (optional).\n" +
|
|
174
178
|
" Substitutes {req}, {scenario} and {feature_file}, so the\n" +
|
|
175
179
|
" gate can run the scenario under test.\n" +
|
|
@@ -251,6 +255,7 @@ function parseArgs(argv) {
|
|
|
251
255
|
prCmd: "",
|
|
252
256
|
setupCmd: "",
|
|
253
257
|
chain: false,
|
|
258
|
+
profile: "",
|
|
254
259
|
};
|
|
255
260
|
for (let i = 0; i < argv.length; i += 1) {
|
|
256
261
|
const token = argv[i];
|
|
@@ -260,6 +265,9 @@ function parseArgs(argv) {
|
|
|
260
265
|
else if (token === "--agent") {
|
|
261
266
|
args.agent = argv[++i] || "";
|
|
262
267
|
}
|
|
268
|
+
else if (token === "--profile") {
|
|
269
|
+
args.profile = argv[++i] || "";
|
|
270
|
+
}
|
|
263
271
|
else if (token === "--test-cmd") {
|
|
264
272
|
args.testCmd = argv[++i] || "";
|
|
265
273
|
}
|
|
@@ -397,6 +405,14 @@ function runPlan(projectDir) {
|
|
|
397
405
|
// `JSON.parse` only ever throws a SyntaxError.
|
|
398
406
|
throw new Error(`plan produced invalid JSON: ${err.message}`);
|
|
399
407
|
}
|
|
408
|
+
// Read on the project itself: the judged copy has every open change
|
|
409
|
+
// archived into it, and no longer says which requirements were modified.
|
|
410
|
+
const modified = new Set((0, in_flight_1.inFlightRequirements)(projectDir)
|
|
411
|
+
.filter((r) => r.op === "modified")
|
|
412
|
+
.map((r) => r.id));
|
|
413
|
+
if (modified.size > 0 && Array.isArray(parsed.requirements)) {
|
|
414
|
+
parsed.requirements = (0, RequirementPlan_1.withRework)(parsed.requirements, modified);
|
|
415
|
+
}
|
|
400
416
|
return parsed;
|
|
401
417
|
}
|
|
402
418
|
// Generous maxBuffer for captured subprocess output — Maven/Gradle first runs
|
|
@@ -502,6 +518,21 @@ function runGate(worktreeDir, testCmd, timeoutMs, req = {}, settings = {}, repor
|
|
|
502
518
|
hint: "",
|
|
503
519
|
};
|
|
504
520
|
}
|
|
521
|
+
// REQ-199: a requirement used on a screen needs an end-to-end test that
|
|
522
|
+
// names it, when the project says where those live.
|
|
523
|
+
if (settings.e2eDir && req.requirement && (0, screen_requirements_1.onScreen)(worktreeDir, req.requirement)) {
|
|
524
|
+
const named = (0, screen_requirements_1.e2eTestsNaming)(worktreeDir, settings.e2eDir, req.requirement);
|
|
525
|
+
report("e2e test", named.length ? "pass" : "fail");
|
|
526
|
+
if (named.length === 0) {
|
|
527
|
+
return {
|
|
528
|
+
ok: false,
|
|
529
|
+
stage: "e2e test",
|
|
530
|
+
output: `No file under ${settings.e2eDir} names ${req.requirement}.`,
|
|
531
|
+
hint: `${req.requirement} is used on a screen: add an end-to-end test under ` +
|
|
532
|
+
`${settings.e2eDir} that names it, driving the screen its scenarios describe.`,
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
}
|
|
505
536
|
// ADR-0028: the architecture check runs between validation and the tests,
|
|
506
537
|
// and its failure goes back to the agent like any other gate failure.
|
|
507
538
|
const archCmd = String(settings.archCmd || "").trim();
|
|
@@ -529,7 +560,10 @@ function runGate(worktreeDir, testCmd, timeoutMs, req = {}, settings = {}, repor
|
|
|
529
560
|
// Where the message stream will be, if there is one to have.
|
|
530
561
|
let reportPath = settings.messageReport || "";
|
|
531
562
|
if (!reportPath && (0, CucumberMessages_1.invokesCucumberDirectly)(resolved)) {
|
|
532
|
-
|
|
563
|
+
// Forward slashes on every platform: Cucumber reads `message:.harness\x`
|
|
564
|
+
// on Windows as a path it does not write, and the gate then had no
|
|
565
|
+
// stream to check and passed a run of no scenarios.
|
|
566
|
+
reportPath = path.posix.join(".harness", `messages-${req.requirement || "run"}-${crypto.randomBytes(3).toString("hex")}.ndjson`);
|
|
533
567
|
fs.mkdirSync(path.join(worktreeDir, ".harness"), { recursive: true });
|
|
534
568
|
resolved = (0, CucumberMessages_1.withMessageReport)(resolved, reportPath);
|
|
535
569
|
}
|
|
@@ -951,7 +985,7 @@ function attemptRequirement(req, ctx) {
|
|
|
951
985
|
event({
|
|
952
986
|
type: "attempt",
|
|
953
987
|
state: "end",
|
|
954
|
-
result: stage === "pass" ? "pass" : "fail",
|
|
988
|
+
result: stage === "pass" ? "pass" : stage === "question" ? "question" : "fail",
|
|
955
989
|
...(stage === "pass" ? {} : { stage }),
|
|
956
990
|
});
|
|
957
991
|
attemptLog.push({
|
|
@@ -987,6 +1021,9 @@ function attemptRequirement(req, ctx) {
|
|
|
987
1021
|
// commit — the precedent must be work already accepted, not a
|
|
988
1022
|
// sibling requirement this same run happens to be writing.
|
|
989
1023
|
withPrecedents: settings.promptPrecedents === true,
|
|
1024
|
+
e2eDir: settings.e2eDir && (0, screen_requirements_1.onScreen)(worktreeDir, req.requirement)
|
|
1025
|
+
? settings.e2eDir
|
|
1026
|
+
: undefined,
|
|
990
1027
|
}));
|
|
991
1028
|
const promptFile = path.join(os.tmpdir(), `specgate-harness-prompt-${req.requirement}-${crypto.randomBytes(4).toString("hex")}.md`);
|
|
992
1029
|
fs.writeFileSync(promptFile, prompt, "utf8");
|
|
@@ -1082,6 +1119,21 @@ function attemptRequirement(req, ctx) {
|
|
|
1082
1119
|
break;
|
|
1083
1120
|
if (stepFailed)
|
|
1084
1121
|
continue;
|
|
1122
|
+
// The agent asked instead of guessing (REQ-189): another attempt would
|
|
1123
|
+
// meet the same question, and the gate has nothing to judge.
|
|
1124
|
+
const asked = questionAsked(eventsFile, attempt, attemptStart);
|
|
1125
|
+
if (asked) {
|
|
1126
|
+
warn(`${req.requirement}: the agent asked — ${asked}`);
|
|
1127
|
+
record("question");
|
|
1128
|
+
discardWorktreeChanges(worktreeDir);
|
|
1129
|
+
return {
|
|
1130
|
+
result: "question",
|
|
1131
|
+
attempts: attempt - firstAttempt + 1,
|
|
1132
|
+
attemptLog,
|
|
1133
|
+
question: asked,
|
|
1134
|
+
error: `Needs clarification: ${asked}`,
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1085
1137
|
// Before the gate: an agent that wrote nothing cannot have implemented
|
|
1086
1138
|
// anything, and the gate would approve it. See `producedNothing` (H19).
|
|
1087
1139
|
if (producedNothing(worktreeDir)) {
|
|
@@ -1242,6 +1294,55 @@ function attemptRequirement(req, ctx) {
|
|
|
1242
1294
|
attemptLog,
|
|
1243
1295
|
};
|
|
1244
1296
|
}
|
|
1297
|
+
/** The question the agent recorded with `specgate ask` during this attempt, if any (REQ-189). */
|
|
1298
|
+
function questionAsked(eventsFile, attempt, startedMs) {
|
|
1299
|
+
let text = "";
|
|
1300
|
+
try {
|
|
1301
|
+
text = fs.readFileSync(eventsFile, "utf8");
|
|
1302
|
+
}
|
|
1303
|
+
catch {
|
|
1304
|
+
return null;
|
|
1305
|
+
}
|
|
1306
|
+
const since = Math.floor(startedMs / 1000) - 1;
|
|
1307
|
+
let asked = null;
|
|
1308
|
+
for (const line of text.split("\n")) {
|
|
1309
|
+
try {
|
|
1310
|
+
const e = JSON.parse(line);
|
|
1311
|
+
if (e.type === "question" && e.attempt === attempt && Number(e.t) >= since && e.text) {
|
|
1312
|
+
asked = String(e.text);
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
catch {
|
|
1316
|
+
/* a line being written */
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
return asked;
|
|
1320
|
+
}
|
|
1321
|
+
/**
|
|
1322
|
+
* The questions the run's agents asked, written into the project and
|
|
1323
|
+
* committed (REQ-189): the agent could not edit the specification, so the
|
|
1324
|
+
* harness does, in one commit per question, and the requirement waits.
|
|
1325
|
+
*/
|
|
1326
|
+
function recordQuestions(projectDir, results) {
|
|
1327
|
+
for (const r of results) {
|
|
1328
|
+
if (r.result !== "question" || !r.question)
|
|
1329
|
+
continue;
|
|
1330
|
+
const asked = (0, clarification_1.ask)(projectDir, r.requirement, r.question);
|
|
1331
|
+
if (!asked.ok) {
|
|
1332
|
+
warn(`${r.requirement}: could not record its question — ${asked.message}`);
|
|
1333
|
+
continue;
|
|
1334
|
+
}
|
|
1335
|
+
git(projectDir, ["add", "-A", "--", "docs/specs", "spec.md"]);
|
|
1336
|
+
git(projectDir, [
|
|
1337
|
+
"commit",
|
|
1338
|
+
"-q",
|
|
1339
|
+
"-m",
|
|
1340
|
+
`docs(specs): ${r.requirement} needs clarification — ${asked.question}\n\n${r.question}`,
|
|
1341
|
+
]);
|
|
1342
|
+
r.error = `Needs clarification: ${asked.question} in ${asked.file} — specgate answer ${asked.question} "…"`;
|
|
1343
|
+
info(`${r.requirement}: ${asked.question} recorded in ${asked.file}; answer it with specgate answer`);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1245
1346
|
/** Where a run's record lands. Local to the machine — see `writeRunRecord`. */
|
|
1246
1347
|
exports.RUNS_DIR = path.join(".harness", "runs");
|
|
1247
1348
|
/**
|
|
@@ -1887,13 +1988,20 @@ function printReport(results, format, recorded) {
|
|
|
1887
1988
|
fail: `${ui.c.bad}${ui.sym.fail}${ui.c.reset}`,
|
|
1888
1989
|
blocked: `${ui.c.warn}${ui.sym.warn}${ui.c.reset}`,
|
|
1889
1990
|
skipped: `${ui.c.muted}${ui.sym.empty}${ui.c.reset}`,
|
|
1991
|
+
question: `${ui.c.warn}?${ui.c.reset}`,
|
|
1992
|
+
};
|
|
1993
|
+
const COLOR = {
|
|
1994
|
+
pass: ui.c.ok,
|
|
1995
|
+
fail: ui.c.bad,
|
|
1996
|
+
blocked: ui.c.warn,
|
|
1997
|
+
skipped: ui.c.muted,
|
|
1998
|
+
question: ui.c.warn,
|
|
1890
1999
|
};
|
|
1891
|
-
const COLOR = { pass: ui.c.ok, fail: ui.c.bad, blocked: ui.c.warn, skipped: ui.c.muted };
|
|
1892
2000
|
const idWidth = Math.max(9, ...results.map((r) => String(r.requirement).length));
|
|
1893
2001
|
process.stdout.write("\n" + ui.section("Harness report"));
|
|
1894
2002
|
for (const r of results) {
|
|
1895
2003
|
process.stdout.write(` ${MARK[r.result] || MARK.fail} ${ui.c.bold}${ui.pad(String(r.requirement), idWidth)}${ui.c.reset} ` +
|
|
1896
|
-
`${COLOR[r.result] || ""}${ui.pad(r.result,
|
|
2004
|
+
`${COLOR[r.result] || ""}${ui.pad(r.result, 9)}${ui.c.reset}` +
|
|
1897
2005
|
ui.muted(`${r.attempts} attempt${r.attempts === 1 ? "" : "s"}`) +
|
|
1898
2006
|
` ${ui.c.accent}${ui.sym.arrow} ${r.branch}${ui.c.reset}\n`);
|
|
1899
2007
|
const sub = (t) => process.stdout.write(` ${t}\n`);
|
|
@@ -2014,6 +2122,14 @@ async function runChain(pending, ctx, hintByReq) {
|
|
|
2014
2122
|
results.push(notAttempted(rest, rest.requirement, reason));
|
|
2015
2123
|
}
|
|
2016
2124
|
};
|
|
2125
|
+
// REQ-209: a stop asked while the last requirement ran starts no other.
|
|
2126
|
+
if (i > 0 && (0, harness_guard_1.takeStop)(projectDir)) {
|
|
2127
|
+
for (const rest of pending.slice(i)) {
|
|
2128
|
+
results.push(notAttempted(rest, rest.requirement, "a stop was asked"));
|
|
2129
|
+
}
|
|
2130
|
+
info(`stopped as asked: ${pending.length - i} requirement(s) not attempted`);
|
|
2131
|
+
break;
|
|
2132
|
+
}
|
|
2017
2133
|
const [result] = await runLevels([req], ctx, {
|
|
2018
2134
|
concurrency: 1,
|
|
2019
2135
|
hintByReq,
|
|
@@ -2319,6 +2435,11 @@ async function runLevels(pending, ctx, opts) {
|
|
|
2319
2435
|
mayStart: () => {
|
|
2320
2436
|
if (stoppedBy)
|
|
2321
2437
|
return false;
|
|
2438
|
+
// REQ-209: a stop asked while a requirement ran starts no other.
|
|
2439
|
+
if (started > 0 && (0, harness_guard_1.takeStop)(ctx.projectDir)) {
|
|
2440
|
+
stoppedBy = { stop: true, code: "stop_asked", reason: "a stop was asked" };
|
|
2441
|
+
return false;
|
|
2442
|
+
}
|
|
2322
2443
|
const verdict = (0, RunBudget_1.budgetVerdict)(limits, { startedAt: budgetStartedAt, started }, Date.now());
|
|
2323
2444
|
if (verdict.stop) {
|
|
2324
2445
|
stoppedBy = verdict;
|
|
@@ -2378,6 +2499,9 @@ class RunCommand extends command_1.BaseCommand {
|
|
|
2378
2499
|
const args = parseArgs(this.args);
|
|
2379
2500
|
setJsonMode(args.format === "json");
|
|
2380
2501
|
const projectDir = (0, project_root_1.resolveProjectDir)(args.projectDir, { requireSentinel: true });
|
|
2502
|
+
// REQ-211: a profile chosen for this run is its agent, ahead of the file's.
|
|
2503
|
+
if (args.profile && !args.agent)
|
|
2504
|
+
args.agent = (0, HarnessConfigFile_1.resolveProfileAgent)(projectDir, args.profile);
|
|
2381
2505
|
const fileConfig = (0, HarnessConfigFile_1.readHarnessConfig)(projectDir);
|
|
2382
2506
|
const settings = (0, HarnessConfigFile_1.resolveHarnessSettings)(fileConfig, args);
|
|
2383
2507
|
// A fresh worktree has no dependencies. Unless told, install what the
|
|
@@ -2390,7 +2514,7 @@ class RunCommand extends command_1.BaseCommand {
|
|
|
2390
2514
|
// may declare profiles without ever writing a `harness.config.yaml`.
|
|
2391
2515
|
// Reaching them through the config reader made them silently absent
|
|
2392
2516
|
// there — the same gap the cost hints had, found the same way.
|
|
2393
|
-
if (settings.profileRules.length === 0) {
|
|
2517
|
+
if (settings.profileRules.length === 0 && !args.profile) {
|
|
2394
2518
|
const rules = (0, HarnessConfigFile_1.readProfileRules)(projectDir);
|
|
2395
2519
|
if (rules.length > 0) {
|
|
2396
2520
|
settings.profileRules = rules;
|
|
@@ -2441,6 +2565,7 @@ class RunCommand extends command_1.BaseCommand {
|
|
|
2441
2565
|
const hintByReq = new Map((plan.next_steps || []).map((s) => [s.requirement, s.hint]));
|
|
2442
2566
|
if (args.dryRun) {
|
|
2443
2567
|
process.stdout.write(ui.header("harness run", `dry run · ${pending.length} requirement(s) would be processed`) +
|
|
2568
|
+
(settings.agent ? ui.kv("Agent", settings.agent, 14) : "") +
|
|
2444
2569
|
ui.kv("Setup", settings.setupCmd || "none", 14) +
|
|
2445
2570
|
(args.chain ? ui.kv("Chain", "each green requirement merged into this branch", 14) : ""));
|
|
2446
2571
|
for (const req of pending) {
|
|
@@ -2537,6 +2662,8 @@ class RunCommand extends command_1.BaseCommand {
|
|
|
2537
2662
|
(holder ? ` on ${holder.reqs.join(", ")}` : "") +
|
|
2538
2663
|
`. Wait for it to finish: its post-merge gate runs in this working copy.`);
|
|
2539
2664
|
}
|
|
2665
|
+
// A stop left from an earlier run is not this run's (REQ-209).
|
|
2666
|
+
(0, harness_guard_1.takeStop)(projectDir);
|
|
2540
2667
|
}
|
|
2541
2668
|
// What the live view lists as up next (REQ-182).
|
|
2542
2669
|
if (!process.env.SPECGATE_HARNESS_WORKER) {
|
|
@@ -2550,6 +2677,9 @@ class RunCommand extends command_1.BaseCommand {
|
|
|
2550
2677
|
hintByReq,
|
|
2551
2678
|
runOne: dispatchLevel,
|
|
2552
2679
|
});
|
|
2680
|
+
// What the agents asked goes into the project, once the run is done (REQ-189).
|
|
2681
|
+
if (!process.env.SPECGATE_HARNESS_WORKER)
|
|
2682
|
+
recordQuestions(projectDir, results);
|
|
2553
2683
|
// Only the run that a person started writes a record. A worker is one
|
|
2554
2684
|
// slice of that run, and its own record would be a second copy of the
|
|
2555
2685
|
// same requirement — `harness report` reads every file in the directory,
|