gsd-pi 2.63.0-dev.d04bbc5 → 2.64.0-dev.9c14bd0
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/dist/resources/extensions/gsd/auto-dashboard.js +5 -5
- package/dist/resources/extensions/gsd/auto-post-unit.js +98 -1
- package/dist/resources/extensions/gsd/auto-verification.js +138 -1
- package/dist/resources/extensions/gsd/auto.js +5 -0
- package/dist/resources/extensions/gsd/post-execution-checks.js +407 -0
- package/dist/resources/extensions/gsd/pre-execution-checks.js +464 -0
- package/dist/resources/extensions/gsd/preferences-types.js +4 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +33 -0
- package/dist/resources/extensions/gsd/preferences.js +4 -0
- package/dist/resources/extensions/gsd/verification-evidence.js +18 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto-dashboard.ts +5 -4
- package/src/resources/extensions/gsd/auto-post-unit.ts +122 -0
- package/src/resources/extensions/gsd/auto-verification.ts +190 -2
- package/src/resources/extensions/gsd/auto.ts +4 -0
- package/src/resources/extensions/gsd/post-execution-checks.ts +539 -0
- package/src/resources/extensions/gsd/pre-execution-checks.ts +573 -0
- package/src/resources/extensions/gsd/preferences-types.ts +28 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +33 -0
- package/src/resources/extensions/gsd/preferences.ts +4 -0
- package/src/resources/extensions/gsd/tests/auto-start-time-persistence.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/enhanced-verification-integration.test.ts +526 -0
- package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +312 -0
- package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +813 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +999 -0
- package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +266 -0
- package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +457 -0
- package/src/resources/extensions/gsd/verification-evidence.ts +68 -0
- /package/dist/web/standalone/.next/static/{vIq9fmvRUaFOpguoX5j4W → SoxM61WC_ia7R2gk4VMpJ}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{vIq9fmvRUaFOpguoX5j4W → SoxM61WC_ia7R2gk4VMpJ}/_ssgManifest.js +0 -0
|
@@ -472,11 +472,11 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
|
|
|
472
472
|
: "";
|
|
473
473
|
lines.push(rightAlign(headerLeft, headerRight, width));
|
|
474
474
|
// Worktree/branch right-aligned below header
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
lines.push(rightAlign("", theme.fg("dim",
|
|
475
|
+
const branchLabel = worktreeName && cachedBranch
|
|
476
|
+
? `${worktreeName} (${cachedBranch})`
|
|
477
|
+
: cachedBranch ?? "";
|
|
478
|
+
if (branchLabel) {
|
|
479
|
+
lines.push(rightAlign("", theme.fg("dim", branchLabel), width));
|
|
480
480
|
}
|
|
481
481
|
// Show health signal details when degraded (yellow/red)
|
|
482
482
|
if (score.level !== "green" && score.signals.length > 0 && widgetMode !== "min") {
|
|
@@ -14,7 +14,7 @@ import { deriveState } from "./state.js";
|
|
|
14
14
|
import { logWarning, logError } from "./workflow-logger.js";
|
|
15
15
|
import { loadFile, parseSummary, resolveAllOverrides } from "./files.js";
|
|
16
16
|
import { loadPrompt } from "./prompt-loader.js";
|
|
17
|
-
import { resolveSliceFile, resolveTaskFile, resolveMilestoneFile, resolveTasksDir, buildTaskFileName, } from "./paths.js";
|
|
17
|
+
import { resolveSliceFile, resolveSlicePath, resolveTaskFile, resolveMilestoneFile, resolveTasksDir, buildTaskFileName, } from "./paths.js";
|
|
18
18
|
import { invalidateAllCaches } from "./cache.js";
|
|
19
19
|
import { parseUnitId } from "./unit-id.js";
|
|
20
20
|
import { closeoutUnit } from "./auto-unit-closeout.js";
|
|
@@ -36,6 +36,10 @@ import { validateFileChanges } from "./safety/file-change-validator.js";
|
|
|
36
36
|
import { validateContent } from "./safety/content-validator.js";
|
|
37
37
|
import { resolveSafetyHarnessConfig } from "./safety/safety-harness.js";
|
|
38
38
|
import { resolveExpectedArtifactPath as resolveArtifactForContent } from "./auto-artifact-paths.js";
|
|
39
|
+
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
40
|
+
import { getSliceTasks } from "./gsd-db.js";
|
|
41
|
+
import { runPreExecutionChecks } from "./pre-execution-checks.js";
|
|
42
|
+
import { writePreExecutionEvidence } from "./verification-evidence.js";
|
|
39
43
|
/** Maximum verification retry attempts before escalating to blocker placeholder (#2653). */
|
|
40
44
|
const MAX_VERIFICATION_RETRIES = 3;
|
|
41
45
|
/** Enqueue a sidecar item (hook, triage, or quick-task) for the main loop to
|
|
@@ -634,6 +638,99 @@ export async function postUnitPostVerification(pctx) {
|
|
|
634
638
|
debugLog("postUnit", { phase: "capture-protection-error", error: String(e) });
|
|
635
639
|
}
|
|
636
640
|
}
|
|
641
|
+
// ── Pre-execution checks (after plan-slice completes) ──
|
|
642
|
+
if (s.currentUnit &&
|
|
643
|
+
s.currentUnit.type === "plan-slice") {
|
|
644
|
+
let preExecPauseNeeded = false;
|
|
645
|
+
await runSafely("postUnitPostVerification", "pre-execution-checks", async () => {
|
|
646
|
+
try {
|
|
647
|
+
// Check preferences — respect enhanced_verification and enhanced_verification_pre
|
|
648
|
+
const prefs = loadEffectiveGSDPreferences()?.preferences;
|
|
649
|
+
const enhancedEnabled = prefs?.enhanced_verification !== false; // default true
|
|
650
|
+
const preEnabled = prefs?.enhanced_verification_pre !== false; // default true
|
|
651
|
+
if (!enhancedEnabled || !preEnabled) {
|
|
652
|
+
debugLog("postUnitPostVerification", {
|
|
653
|
+
phase: "pre-execution-checks",
|
|
654
|
+
skipped: true,
|
|
655
|
+
reason: "disabled by preferences",
|
|
656
|
+
});
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
// Parse the unit ID to get milestone/slice IDs
|
|
660
|
+
const { milestone: mid, slice: sid } = parseUnitId(s.currentUnit.id);
|
|
661
|
+
if (!mid || !sid) {
|
|
662
|
+
debugLog("postUnitPostVerification", {
|
|
663
|
+
phase: "pre-execution-checks",
|
|
664
|
+
skipped: true,
|
|
665
|
+
reason: "could not parse milestone/slice from unit ID",
|
|
666
|
+
});
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
// Get tasks for this slice from DB
|
|
670
|
+
const tasks = getSliceTasks(mid, sid);
|
|
671
|
+
if (tasks.length === 0) {
|
|
672
|
+
debugLog("postUnitPostVerification", {
|
|
673
|
+
phase: "pre-execution-checks",
|
|
674
|
+
skipped: true,
|
|
675
|
+
reason: "no tasks found for slice",
|
|
676
|
+
});
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
// Run pre-execution checks
|
|
680
|
+
const result = await runPreExecutionChecks(tasks, s.basePath);
|
|
681
|
+
// Log summary to stderr in existing verification output format
|
|
682
|
+
const emoji = result.status === "pass" ? "✅" : result.status === "warn" ? "⚠️" : "❌";
|
|
683
|
+
process.stderr.write(`gsd-pre-exec: ${emoji} Pre-execution checks ${result.status} for ${mid}/${sid} (${result.durationMs}ms)\n`);
|
|
684
|
+
// Log individual check results
|
|
685
|
+
for (const check of result.checks) {
|
|
686
|
+
const checkEmoji = check.passed ? "✓" : check.blocking ? "✗" : "⚠";
|
|
687
|
+
process.stderr.write(`gsd-pre-exec: ${checkEmoji} [${check.category}] ${check.target}: ${check.message}\n`);
|
|
688
|
+
}
|
|
689
|
+
// Write evidence JSON to slice artifacts directory
|
|
690
|
+
const slicePath = resolveSlicePath(s.basePath, mid, sid);
|
|
691
|
+
if (slicePath) {
|
|
692
|
+
writePreExecutionEvidence(result, slicePath, mid, sid);
|
|
693
|
+
}
|
|
694
|
+
// Notify UI
|
|
695
|
+
if (result.status === "fail") {
|
|
696
|
+
const blockingCount = result.checks.filter(c => !c.passed && c.blocking).length;
|
|
697
|
+
ctx.ui.notify(`Pre-execution checks failed: ${blockingCount} blocking issue${blockingCount === 1 ? "" : "s"} found`, "error");
|
|
698
|
+
preExecPauseNeeded = true;
|
|
699
|
+
}
|
|
700
|
+
else if (result.status === "warn") {
|
|
701
|
+
ctx.ui.notify(`Pre-execution checks passed with warnings`, "warning");
|
|
702
|
+
// Strict mode: treat warnings as blocking
|
|
703
|
+
if (prefs?.enhanced_verification_strict === true) {
|
|
704
|
+
preExecPauseNeeded = true;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
debugLog("postUnitPostVerification", {
|
|
708
|
+
phase: "pre-execution-checks",
|
|
709
|
+
status: result.status,
|
|
710
|
+
checkCount: result.checks.length,
|
|
711
|
+
durationMs: result.durationMs,
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
catch (preExecError) {
|
|
715
|
+
// Fail-closed: if runPreExecutionChecks throws, pause auto-mode instead of silently continuing
|
|
716
|
+
const errorMessage = preExecError instanceof Error ? preExecError.message : String(preExecError);
|
|
717
|
+
debugLog("postUnitPostVerification", {
|
|
718
|
+
phase: "pre-execution-checks",
|
|
719
|
+
error: errorMessage,
|
|
720
|
+
failClosed: true,
|
|
721
|
+
});
|
|
722
|
+
logError("engine", `gsd-pre-exec: Pre-execution checks threw an error: ${errorMessage}`);
|
|
723
|
+
ctx.ui.notify(`Pre-execution checks error: ${errorMessage} — pausing for human review`, "error");
|
|
724
|
+
preExecPauseNeeded = true;
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
// Check for blocking failures after runSafely completes
|
|
728
|
+
if (preExecPauseNeeded) {
|
|
729
|
+
debugLog("postUnitPostVerification", { phase: "pre-execution-checks", pausing: true, reason: "blocking failures detected" });
|
|
730
|
+
await pauseAuto(ctx, pi);
|
|
731
|
+
return "stopped";
|
|
732
|
+
}
|
|
733
|
+
}
|
|
637
734
|
// ── Triage check ──
|
|
638
735
|
if (!s.stepMode &&
|
|
639
736
|
s.currentUnit &&
|
|
@@ -9,13 +9,15 @@
|
|
|
9
9
|
* value instead of calling return/pauseAuto directly — the caller
|
|
10
10
|
* checks the result and handles control flow.
|
|
11
11
|
*/
|
|
12
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
12
13
|
import { resolveSlicePath } from "./paths.js";
|
|
13
14
|
import { parseUnitId } from "./unit-id.js";
|
|
14
|
-
import { isDbAvailable, getTask } from "./gsd-db.js";
|
|
15
|
+
import { isDbAvailable, getTask, getSliceTasks } from "./gsd-db.js";
|
|
15
16
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
16
17
|
import { runVerificationGate, formatFailureContext, captureRuntimeErrors, runDependencyAudit, } from "./verification-gate.js";
|
|
17
18
|
import { writeVerificationJSON } from "./verification-evidence.js";
|
|
18
19
|
import { logWarning } from "./workflow-logger.js";
|
|
20
|
+
import { runPostExecutionChecks } from "./post-execution-checks.js";
|
|
19
21
|
import { join } from "node:path";
|
|
20
22
|
function isInfraVerificationFailure(stderr) {
|
|
21
23
|
return /\b(ENOENT|ENOTFOUND|ETIMEDOUT|ECONNRESET|EAI_AGAIN|spawn\s+\S+\s+ENOENT|command not found)\b/i.test(stderr);
|
|
@@ -128,12 +130,104 @@ export async function runPostUnitVerification(vctx, pauseAuto) {
|
|
|
128
130
|
: "Verification failed due to infrastructure/runtime environment issues — treating as advisory.", "warning");
|
|
129
131
|
return "continue";
|
|
130
132
|
}
|
|
133
|
+
// ── Post-execution checks (run after main verification passes for execute-task units) ──
|
|
134
|
+
let postExecChecks;
|
|
135
|
+
let postExecBlockingFailure = false;
|
|
136
|
+
if (result.passed && mid && sid && tid) {
|
|
137
|
+
// Check preferences — respect enhanced_verification and enhanced_verification_post
|
|
138
|
+
const enhancedEnabled = prefs?.enhanced_verification !== false; // default true
|
|
139
|
+
const postEnabled = prefs?.enhanced_verification_post !== false; // default true
|
|
140
|
+
if (enhancedEnabled && postEnabled && isDbAvailable()) {
|
|
141
|
+
try {
|
|
142
|
+
// Get the completed task from DB
|
|
143
|
+
const taskRow = getTask(mid, sid, tid);
|
|
144
|
+
if (taskRow && taskRow.key_files && taskRow.key_files.length > 0) {
|
|
145
|
+
// Get all tasks in the slice
|
|
146
|
+
const allTasks = getSliceTasks(mid, sid);
|
|
147
|
+
// Filter to prior completed tasks (status = 'complete' or 'done', before current task)
|
|
148
|
+
const priorTasks = allTasks.filter((t) => (t.status === "complete" || t.status === "done") &&
|
|
149
|
+
t.id !== tid &&
|
|
150
|
+
t.sequence < taskRow.sequence);
|
|
151
|
+
// Run post-execution checks
|
|
152
|
+
const postExecResult = runPostExecutionChecks(taskRow, priorTasks, s.basePath);
|
|
153
|
+
// Store checks for evidence JSON
|
|
154
|
+
postExecChecks = postExecResult.checks;
|
|
155
|
+
// Log summary to stderr with gsd-post-exec: prefix
|
|
156
|
+
const emoji = postExecResult.status === "pass"
|
|
157
|
+
? "✅"
|
|
158
|
+
: postExecResult.status === "warn"
|
|
159
|
+
? "⚠️"
|
|
160
|
+
: "❌";
|
|
161
|
+
process.stderr.write(`gsd-post-exec: ${emoji} Post-execution checks ${postExecResult.status} for ${mid}/${sid}/${tid} (${postExecResult.durationMs}ms)\n`);
|
|
162
|
+
// Log individual check results
|
|
163
|
+
for (const check of postExecResult.checks) {
|
|
164
|
+
const checkEmoji = check.passed
|
|
165
|
+
? "✓"
|
|
166
|
+
: check.blocking
|
|
167
|
+
? "✗"
|
|
168
|
+
: "⚠";
|
|
169
|
+
process.stderr.write(`gsd-post-exec: ${checkEmoji} [${check.category}] ${check.target}: ${check.message}\n`);
|
|
170
|
+
}
|
|
171
|
+
// Check for blocking failures
|
|
172
|
+
if (postExecResult.status === "fail") {
|
|
173
|
+
postExecBlockingFailure = true;
|
|
174
|
+
const blockingCount = postExecResult.checks.filter((c) => !c.passed && c.blocking).length;
|
|
175
|
+
ctx.ui.notify(`Post-execution checks failed: ${blockingCount} blocking issue${blockingCount === 1 ? "" : "s"} found`, "error");
|
|
176
|
+
}
|
|
177
|
+
else if (postExecResult.status === "warn") {
|
|
178
|
+
ctx.ui.notify(`Post-execution checks passed with warnings`, "warning");
|
|
179
|
+
// Strict mode: treat warnings as blocking
|
|
180
|
+
if (prefs?.enhanced_verification_strict === true) {
|
|
181
|
+
postExecBlockingFailure = true;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch (postExecErr) {
|
|
187
|
+
// Post-execution check errors are non-fatal — log and continue
|
|
188
|
+
logWarning("engine", `gsd-post-exec: error — ${postExecErr.message}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Re-write verification evidence JSON with post-execution checks
|
|
193
|
+
if (postExecChecks && postExecChecks.length > 0 && mid && sid && tid) {
|
|
194
|
+
try {
|
|
195
|
+
const sDir = resolveSlicePath(s.basePath, mid, sid);
|
|
196
|
+
if (sDir) {
|
|
197
|
+
const tasksDir = join(sDir, "tasks");
|
|
198
|
+
// Add postExecutionChecks to the result for the JSON write
|
|
199
|
+
const resultWithPostExec = {
|
|
200
|
+
...result,
|
|
201
|
+
// Mark as failed if there was a blocking post-exec failure
|
|
202
|
+
passed: result.passed && !postExecBlockingFailure,
|
|
203
|
+
};
|
|
204
|
+
// Manually write with postExecutionChecks field
|
|
205
|
+
writeVerificationJSONWithPostExec(resultWithPostExec, tasksDir, tid, s.currentUnit.id, postExecChecks, postExecBlockingFailure ? attempt + 1 : undefined, postExecBlockingFailure ? maxRetries : undefined);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
catch (evidenceErr) {
|
|
209
|
+
logWarning("engine", `verification-evidence: post-exec write error — ${evidenceErr.message}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// Update result.passed based on post-execution checks
|
|
213
|
+
if (postExecBlockingFailure) {
|
|
214
|
+
result.passed = false;
|
|
215
|
+
}
|
|
131
216
|
// ── Auto-fix retry logic ──
|
|
132
217
|
if (result.passed) {
|
|
133
218
|
s.verificationRetryCount.delete(s.currentUnit.id);
|
|
134
219
|
s.pendingVerificationRetry = null;
|
|
135
220
|
return "continue";
|
|
136
221
|
}
|
|
222
|
+
else if (postExecBlockingFailure) {
|
|
223
|
+
// Post-execution failures are cross-task consistency issues — retrying the same task won't fix them.
|
|
224
|
+
// Skip retry and pause immediately for human review.
|
|
225
|
+
s.verificationRetryCount.delete(s.currentUnit.id);
|
|
226
|
+
s.pendingVerificationRetry = null;
|
|
227
|
+
ctx.ui.notify(`Post-execution checks failed — cross-task consistency issue detected, pausing for human review`, "error");
|
|
228
|
+
await pauseAuto(ctx, pi);
|
|
229
|
+
return "pause";
|
|
230
|
+
}
|
|
137
231
|
else if (autoFixEnabled && attempt + 1 <= maxRetries) {
|
|
138
232
|
const nextAttempt = attempt + 1;
|
|
139
233
|
s.verificationRetryCount.set(s.currentUnit.id, nextAttempt);
|
|
@@ -173,3 +267,46 @@ export async function runPostUnitVerification(vctx, pauseAuto) {
|
|
|
173
267
|
return "continue";
|
|
174
268
|
}
|
|
175
269
|
}
|
|
270
|
+
/**
|
|
271
|
+
* Write verification evidence JSON with post-execution checks included.
|
|
272
|
+
* This is a variant of writeVerificationJSON that adds the postExecutionChecks field.
|
|
273
|
+
*/
|
|
274
|
+
function writeVerificationJSONWithPostExec(result, tasksDir, taskId, unitId, postExecutionChecks, retryAttempt, maxRetries) {
|
|
275
|
+
mkdirSync(tasksDir, { recursive: true });
|
|
276
|
+
const evidence = {
|
|
277
|
+
schemaVersion: 1,
|
|
278
|
+
taskId,
|
|
279
|
+
unitId: unitId ?? taskId,
|
|
280
|
+
timestamp: result.timestamp,
|
|
281
|
+
passed: result.passed,
|
|
282
|
+
discoverySource: result.discoverySource,
|
|
283
|
+
checks: result.checks.map((check) => ({
|
|
284
|
+
command: check.command,
|
|
285
|
+
exitCode: check.exitCode,
|
|
286
|
+
durationMs: check.durationMs,
|
|
287
|
+
verdict: check.exitCode === 0 ? "pass" : "fail",
|
|
288
|
+
})),
|
|
289
|
+
...(retryAttempt !== undefined ? { retryAttempt } : {}),
|
|
290
|
+
...(maxRetries !== undefined ? { maxRetries } : {}),
|
|
291
|
+
postExecutionChecks,
|
|
292
|
+
};
|
|
293
|
+
if (result.runtimeErrors && result.runtimeErrors.length > 0) {
|
|
294
|
+
evidence.runtimeErrors = result.runtimeErrors.map(e => ({
|
|
295
|
+
source: e.source,
|
|
296
|
+
severity: e.severity,
|
|
297
|
+
message: e.message,
|
|
298
|
+
blocking: e.blocking,
|
|
299
|
+
}));
|
|
300
|
+
}
|
|
301
|
+
if (result.auditWarnings && result.auditWarnings.length > 0) {
|
|
302
|
+
evidence.auditWarnings = result.auditWarnings.map(w => ({
|
|
303
|
+
name: w.name,
|
|
304
|
+
severity: w.severity,
|
|
305
|
+
title: w.title,
|
|
306
|
+
url: w.url,
|
|
307
|
+
fixAvailable: w.fixAvailable,
|
|
308
|
+
}));
|
|
309
|
+
}
|
|
310
|
+
const filePath = join(tasksDir, `${taskId}-VERIFY.json`);
|
|
311
|
+
writeFileSync(filePath, JSON.stringify(evidence, null, 2) + "\n", "utf-8");
|
|
312
|
+
}
|
|
@@ -643,6 +643,7 @@ export async function pauseAuto(ctx, _pi, _errorContext) {
|
|
|
643
643
|
sessionFile: s.pausedSessionFile,
|
|
644
644
|
activeEngineId: s.activeEngineId,
|
|
645
645
|
activeRunDir: s.activeRunDir,
|
|
646
|
+
autoStartTime: s.autoStartTime,
|
|
646
647
|
};
|
|
647
648
|
const runtimeDir = join(gsdRoot(s.originalBasePath || s.basePath), "runtime");
|
|
648
649
|
mkdirSync(runtimeDir, { recursive: true });
|
|
@@ -842,6 +843,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
842
843
|
s.activeRunDir = meta.activeRunDir ?? null;
|
|
843
844
|
s.originalBasePath = meta.originalBasePath || base;
|
|
844
845
|
s.stepMode = meta.stepMode ?? requestedStepMode;
|
|
846
|
+
s.autoStartTime = meta.autoStartTime || Date.now();
|
|
845
847
|
s.paused = true;
|
|
846
848
|
try {
|
|
847
849
|
unlinkSync(pausedPath);
|
|
@@ -869,6 +871,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
869
871
|
s.currentMilestoneId = meta.milestoneId;
|
|
870
872
|
s.originalBasePath = meta.originalBasePath || base;
|
|
871
873
|
s.stepMode = meta.stepMode ?? requestedStepMode;
|
|
874
|
+
s.autoStartTime = meta.autoStartTime || Date.now();
|
|
872
875
|
s.paused = true;
|
|
873
876
|
// Clean up the persisted file — we're consuming it
|
|
874
877
|
try {
|
|
@@ -900,6 +903,8 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
900
903
|
s.cmdCtx = ctx;
|
|
901
904
|
s.basePath = base;
|
|
902
905
|
setLogBasePath(base);
|
|
906
|
+
if (!s.autoStartTime || s.autoStartTime <= 0)
|
|
907
|
+
s.autoStartTime = Date.now();
|
|
903
908
|
s.unitDispatchCount.clear();
|
|
904
909
|
s.unitLifetimeDispatches.clear();
|
|
905
910
|
if (!getLedger())
|