gsd-pi 2.28.0-dev.704ded6 → 2.28.0-dev.853dfc5

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.
@@ -1,6 +1,6 @@
1
1
  import { DefaultResourceLoader } from '@gsd/pi-coding-agent';
2
2
  import { homedir } from 'node:os';
3
- import { chmodSync, copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'node:fs';
3
+ import { chmodSync, cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'node:fs';
4
4
  import { dirname, join, relative, resolve } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { compareSemver } from './update-check.js';
@@ -111,34 +111,10 @@ function syncResourceDir(srcDir, destDir) {
111
111
  rmSync(target, { recursive: true, force: true });
112
112
  }
113
113
  }
114
- try {
115
- cpSync(srcDir, destDir, { recursive: true, force: true });
116
- }
117
- catch {
118
- // Fallback for Windows paths with non-ASCII characters where cpSync
119
- // fails with the \\?\ extended-length prefix (#1178).
120
- copyDirRecursive(srcDir, destDir);
121
- }
114
+ cpSync(srcDir, destDir, { recursive: true, force: true });
122
115
  makeTreeWritable(destDir);
123
116
  }
124
117
  }
125
- /**
126
- * Recursive directory copy using copyFileSync — workaround for cpSync failures
127
- * on Windows paths containing non-ASCII characters (#1178).
128
- */
129
- function copyDirRecursive(src, dest) {
130
- mkdirSync(dest, { recursive: true });
131
- for (const entry of readdirSync(src, { withFileTypes: true })) {
132
- const srcPath = join(src, entry.name);
133
- const destPath = join(dest, entry.name);
134
- if (entry.isDirectory()) {
135
- copyDirRecursive(srcPath, destPath);
136
- }
137
- else {
138
- copyFileSync(srcPath, destPath);
139
- }
140
- }
141
- }
142
118
  /**
143
119
  * Syncs all bundled resources to agentDir (~/.gsd/agent/) on every launch.
144
120
  *
@@ -105,39 +105,19 @@ export async function runPostUnitVerification(
105
105
  const completionKey = `${s.currentUnit.type}/${s.currentUnit.id}`;
106
106
 
107
107
  if (result.checks.length > 0) {
108
- const blockingChecks = result.checks.filter(c => c.blocking);
109
- const advisoryChecks = result.checks.filter(c => !c.blocking);
110
- const blockingPassCount = blockingChecks.filter(c => c.exitCode === 0).length;
111
- const advisoryFailCount = advisoryChecks.filter(c => c.exitCode !== 0).length;
112
-
108
+ const passCount = result.checks.filter(c => c.exitCode === 0).length;
109
+ const total = result.checks.length;
113
110
  if (result.passed) {
114
- let msg = blockingChecks.length > 0
115
- ? `Verification gate: ${blockingPassCount}/${blockingChecks.length} blocking checks passed`
116
- : `Verification gate: passed (no blocking checks)`;
117
- if (advisoryFailCount > 0) {
118
- msg += ` (${advisoryFailCount} advisory warning${advisoryFailCount > 1 ? "s" : ""})`;
119
- }
120
- ctx.ui.notify(msg);
121
- // Log advisory warnings to stderr for visibility
122
- if (advisoryFailCount > 0) {
123
- const advisoryFailures = advisoryChecks.filter(c => c.exitCode !== 0);
124
- process.stderr.write(`verification-gate: ${advisoryFailCount} advisory (non-blocking) failure(s)\n`);
125
- for (const f of advisoryFailures) {
126
- process.stderr.write(` [advisory] ${f.command} exited ${f.exitCode}\n`);
127
- }
128
- }
111
+ ctx.ui.notify(`Verification gate: ${passCount}/${total} checks passed`);
129
112
  } else {
130
- const blockingFailures = blockingChecks.filter(c => c.exitCode !== 0);
131
- const failNames = blockingFailures.map(f => f.command).join(", ");
113
+ const failures = result.checks.filter(c => c.exitCode !== 0);
114
+ const failNames = failures.map(f => f.command).join(", ");
132
115
  ctx.ui.notify(`Verification gate: FAILED — ${failNames}`);
133
- process.stderr.write(`verification-gate: ${blockingFailures.length}/${blockingChecks.length} blocking checks failed\n`);
134
- for (const f of blockingFailures) {
116
+ process.stderr.write(`verification-gate: ${total - passCount}/${total} checks failed\n`);
117
+ for (const f of failures) {
135
118
  process.stderr.write(` ${f.command} exited ${f.exitCode}\n`);
136
119
  if (f.stderr) process.stderr.write(` stderr: ${f.stderr.slice(0, 500)}\n`);
137
120
  }
138
- if (advisoryFailCount > 0) {
139
- process.stderr.write(`verification-gate: ${advisoryFailCount} additional advisory (non-blocking) failure(s)\n`);
140
- }
141
121
  }
142
122
  }
143
123
 
@@ -58,7 +58,6 @@ test("verification-evidence: writeVerificationJSON writes correct JSON shape", (
58
58
  stdout: "all good",
59
59
  stderr: "",
60
60
  durationMs: 2340,
61
- blocking: true,
62
61
  },
63
62
  ],
64
63
  });
@@ -106,9 +105,9 @@ test("verification-evidence: writeVerificationJSON maps exitCode to verdict corr
106
105
  const result = makeResult({
107
106
  passed: false,
108
107
  checks: [
109
- { command: "lint", exitCode: 0, stdout: "", stderr: "", durationMs: 100, blocking: true },
110
- { command: "test", exitCode: 1, stdout: "", stderr: "fail", durationMs: 200, blocking: true },
111
- { command: "audit", exitCode: 2, stdout: "", stderr: "err", durationMs: 300, blocking: true },
108
+ { command: "lint", exitCode: 0, stdout: "", stderr: "", durationMs: 100 },
109
+ { command: "test", exitCode: 1, stdout: "", stderr: "fail", durationMs: 200 },
110
+ { command: "audit", exitCode: 2, stdout: "", stderr: "err", durationMs: 300 },
112
111
  ],
113
112
  });
114
113
 
@@ -134,7 +133,6 @@ test("verification-evidence: writeVerificationJSON excludes stdout/stderr from o
134
133
  stdout: "hello\n",
135
134
  stderr: "some warning",
136
135
  durationMs: 50,
137
- blocking: true,
138
136
  },
139
137
  ],
140
138
  });
@@ -183,8 +181,8 @@ test("verification-evidence: writeVerificationJSON uses optional unitId when pro
183
181
  test("verification-evidence: formatEvidenceTable returns markdown table with correct columns", () => {
184
182
  const result = makeResult({
185
183
  checks: [
186
- { command: "npm run typecheck", exitCode: 0, stdout: "", stderr: "", durationMs: 2340, blocking: true },
187
- { command: "npm run lint", exitCode: 1, stdout: "", stderr: "err", durationMs: 1100, blocking: true },
184
+ { command: "npm run typecheck", exitCode: 0, stdout: "", stderr: "", durationMs: 2340 },
185
+ { command: "npm run lint", exitCode: 1, stdout: "", stderr: "err", durationMs: 1100 },
188
186
  ],
189
187
  });
190
188
 
@@ -216,9 +214,9 @@ test("verification-evidence: formatEvidenceTable returns no-checks message for e
216
214
  test("verification-evidence: formatEvidenceTable formats duration as seconds with 1 decimal", () => {
217
215
  const result = makeResult({
218
216
  checks: [
219
- { command: "fast", exitCode: 0, stdout: "", stderr: "", durationMs: 150, blocking: true },
220
- { command: "slow", exitCode: 0, stdout: "", stderr: "", durationMs: 2340, blocking: true },
221
- { command: "zero", exitCode: 0, stdout: "", stderr: "", durationMs: 0, blocking: true },
217
+ { command: "fast", exitCode: 0, stdout: "", stderr: "", durationMs: 150 },
218
+ { command: "slow", exitCode: 0, stdout: "", stderr: "", durationMs: 2340 },
219
+ { command: "zero", exitCode: 0, stdout: "", stderr: "", durationMs: 0 },
222
220
  ],
223
221
  });
224
222
 
@@ -232,8 +230,8 @@ test("verification-evidence: formatEvidenceTable uses ✅/❌ emoji for pass/fai
232
230
  const result = makeResult({
233
231
  passed: false,
234
232
  checks: [
235
- { command: "pass-cmd", exitCode: 0, stdout: "", stderr: "", durationMs: 100, blocking: true },
236
- { command: "fail-cmd", exitCode: 1, stdout: "", stderr: "", durationMs: 200, blocking: true },
233
+ { command: "pass-cmd", exitCode: 0, stdout: "", stderr: "", durationMs: 100 },
234
+ { command: "fail-cmd", exitCode: 1, stdout: "", stderr: "", durationMs: 200 },
237
235
  ],
238
236
  });
239
237
 
@@ -337,8 +335,8 @@ test("verification-evidence: integration — VerificationResult → JSON → tab
337
335
  const result = makeResult({
338
336
  passed: false,
339
337
  checks: [
340
- { command: "npm run typecheck", exitCode: 0, stdout: "ok", stderr: "", durationMs: 1500, blocking: true },
341
- { command: "npm run test:unit", exitCode: 1, stdout: "", stderr: "1 failed", durationMs: 3200, blocking: true },
338
+ { command: "npm run typecheck", exitCode: 0, stdout: "ok", stderr: "", durationMs: 1500 },
339
+ { command: "npm run test:unit", exitCode: 1, stdout: "", stderr: "1 failed", durationMs: 3200 },
342
340
  ],
343
341
  discoverySource: "package-json",
344
342
  });
@@ -392,7 +390,7 @@ test("verification-evidence: writeVerificationJSON with retryAttempt and maxRetr
392
390
  const result = makeResult({
393
391
  passed: false,
394
392
  checks: [
395
- { command: "npm run lint", exitCode: 1, stdout: "", stderr: "error", durationMs: 300, blocking: true },
393
+ { command: "npm run lint", exitCode: 1, stdout: "", stderr: "error", durationMs: 300 },
396
394
  ],
397
395
  });
398
396
 
@@ -417,7 +415,7 @@ test("verification-evidence: writeVerificationJSON without retry params omits re
417
415
  const result = makeResult({
418
416
  passed: true,
419
417
  checks: [
420
- { command: "npm run test", exitCode: 0, stdout: "ok", stderr: "", durationMs: 100, blocking: true },
418
+ { command: "npm run test", exitCode: 0, stdout: "ok", stderr: "", durationMs: 100 },
421
419
  ],
422
420
  });
423
421
 
@@ -443,7 +441,7 @@ test("verification-evidence: writeVerificationJSON includes runtimeErrors when p
443
441
  const result = makeResult({
444
442
  passed: false,
445
443
  checks: [
446
- { command: "npm run test", exitCode: 0, stdout: "ok", stderr: "", durationMs: 100, blocking: true },
444
+ { command: "npm run test", exitCode: 0, stdout: "ok", stderr: "", durationMs: 100 },
447
445
  ],
448
446
  runtimeErrors: [
449
447
  { source: "bg-shell", severity: "crash", message: "Server crashed", blocking: true },
@@ -475,7 +473,7 @@ test("verification-evidence: writeVerificationJSON omits runtimeErrors when abse
475
473
  const result = makeResult({
476
474
  passed: true,
477
475
  checks: [
478
- { command: "npm run lint", exitCode: 0, stdout: "", stderr: "", durationMs: 50, blocking: true },
476
+ { command: "npm run lint", exitCode: 0, stdout: "", stderr: "", durationMs: 50 },
479
477
  ],
480
478
  });
481
479
 
@@ -514,7 +512,7 @@ test("verification-evidence: formatEvidenceTable appends runtime errors section"
514
512
  const result = makeResult({
515
513
  passed: false,
516
514
  checks: [
517
- { command: "npm run test", exitCode: 0, stdout: "", stderr: "", durationMs: 100, blocking: true },
515
+ { command: "npm run test", exitCode: 0, stdout: "", stderr: "", durationMs: 100 },
518
516
  ],
519
517
  runtimeErrors: [
520
518
  { source: "bg-shell", severity: "crash", message: "Server crashed with SIGKILL", blocking: true },
@@ -539,7 +537,7 @@ test("verification-evidence: formatEvidenceTable omits runtime errors section wh
539
537
  const result = makeResult({
540
538
  passed: true,
541
539
  checks: [
542
- { command: "npm run lint", exitCode: 0, stdout: "", stderr: "", durationMs: 200, blocking: true },
540
+ { command: "npm run lint", exitCode: 0, stdout: "", stderr: "", durationMs: 200 },
543
541
  ],
544
542
  });
545
543
 
@@ -554,7 +552,7 @@ test("verification-evidence: formatEvidenceTable truncates runtime error message
554
552
  const result = makeResult({
555
553
  passed: false,
556
554
  checks: [
557
- { command: "npm run test", exitCode: 0, stdout: "", stderr: "", durationMs: 100, blocking: true },
555
+ { command: "npm run test", exitCode: 0, stdout: "", stderr: "", durationMs: 100 },
558
556
  ],
559
557
  runtimeErrors: [
560
558
  { source: "bg-shell", severity: "error", message: longMessage, blocking: false },
@@ -600,7 +598,7 @@ test("verification-evidence: writeVerificationJSON includes auditWarnings when p
600
598
  const result = makeResult({
601
599
  passed: true,
602
600
  checks: [
603
- { command: "npm run test", exitCode: 0, stdout: "ok", stderr: "", durationMs: 100, blocking: true },
601
+ { command: "npm run test", exitCode: 0, stdout: "ok", stderr: "", durationMs: 100 },
604
602
  ],
605
603
  auditWarnings: SAMPLE_AUDIT_WARNINGS,
606
604
  });
@@ -629,7 +627,7 @@ test("verification-evidence: writeVerificationJSON omits auditWarnings when abse
629
627
  const result = makeResult({
630
628
  passed: true,
631
629
  checks: [
632
- { command: "npm run lint", exitCode: 0, stdout: "", stderr: "", durationMs: 50, blocking: true },
630
+ { command: "npm run lint", exitCode: 0, stdout: "", stderr: "", durationMs: 50 },
633
631
  ],
634
632
  });
635
633
 
@@ -668,7 +666,7 @@ test("verification-evidence: formatEvidenceTable appends audit warnings section"
668
666
  const result = makeResult({
669
667
  passed: true,
670
668
  checks: [
671
- { command: "npm run test", exitCode: 0, stdout: "", stderr: "", durationMs: 100, blocking: true },
669
+ { command: "npm run test", exitCode: 0, stdout: "", stderr: "", durationMs: 100 },
672
670
  ],
673
671
  auditWarnings: SAMPLE_AUDIT_WARNINGS,
674
672
  });
@@ -691,7 +689,7 @@ test("verification-evidence: formatEvidenceTable omits audit warnings section wh
691
689
  const result = makeResult({
692
690
  passed: true,
693
691
  checks: [
694
- { command: "npm run lint", exitCode: 0, stdout: "", stderr: "", durationMs: 200, blocking: true },
692
+ { command: "npm run lint", exitCode: 0, stdout: "", stderr: "", durationMs: 200 },
695
693
  ],
696
694
  });
697
695
 
@@ -707,7 +705,7 @@ test("verification-evidence: integration — VerificationResult with auditWarnin
707
705
  const result = makeResult({
708
706
  passed: true,
709
707
  checks: [
710
- { command: "npm run typecheck", exitCode: 0, stdout: "ok", stderr: "", durationMs: 1500, blocking: true },
708
+ { command: "npm run typecheck", exitCode: 0, stdout: "ok", stderr: "", durationMs: 1500 },
711
709
  ],
712
710
  auditWarnings: [
713
711
  {
@@ -581,7 +581,7 @@ test("formatFailureContext: formats a single failure with command, exit code, st
581
581
  const result: import("../types.ts").VerificationResult = {
582
582
  passed: false,
583
583
  checks: [
584
- { command: "npm run lint", exitCode: 1, stdout: "", stderr: "error: unused var", durationMs: 500, blocking: true },
584
+ { command: "npm run lint", exitCode: 1, stdout: "", stderr: "error: unused var", durationMs: 500 },
585
585
  ],
586
586
  discoverySource: "preference",
587
587
  timestamp: Date.now(),
@@ -598,9 +598,9 @@ test("formatFailureContext: formats multiple failures", () => {
598
598
  const result: import("../types.ts").VerificationResult = {
599
599
  passed: false,
600
600
  checks: [
601
- { command: "npm run lint", exitCode: 1, stdout: "", stderr: "lint error", durationMs: 100, blocking: true },
602
- { command: "npm run test", exitCode: 2, stdout: "", stderr: "test failure", durationMs: 200, blocking: true },
603
- { command: "npm run typecheck", exitCode: 0, stdout: "ok", stderr: "", durationMs: 50, blocking: true },
601
+ { command: "npm run lint", exitCode: 1, stdout: "", stderr: "lint error", durationMs: 100 },
602
+ { command: "npm run test", exitCode: 2, stdout: "", stderr: "test failure", durationMs: 200 },
603
+ { command: "npm run typecheck", exitCode: 0, stdout: "ok", stderr: "", durationMs: 50 },
604
604
  ],
605
605
  discoverySource: "preference",
606
606
  timestamp: Date.now(),
@@ -619,7 +619,7 @@ test("formatFailureContext: truncates stderr longer than 2000 chars", () => {
619
619
  const result: import("../types.ts").VerificationResult = {
620
620
  passed: false,
621
621
  checks: [
622
- { command: "big-err", exitCode: 1, stdout: "", stderr: longStderr, durationMs: 100, blocking: true },
622
+ { command: "big-err", exitCode: 1, stdout: "", stderr: longStderr, durationMs: 100 },
623
623
  ],
624
624
  discoverySource: "preference",
625
625
  timestamp: Date.now(),
@@ -634,8 +634,8 @@ test("formatFailureContext: returns empty string when all checks pass", () => {
634
634
  const result: import("../types.ts").VerificationResult = {
635
635
  passed: true,
636
636
  checks: [
637
- { command: "npm run lint", exitCode: 0, stdout: "ok", stderr: "", durationMs: 100, blocking: true },
638
- { command: "npm run test", exitCode: 0, stdout: "ok", stderr: "", durationMs: 200, blocking: true },
637
+ { command: "npm run lint", exitCode: 0, stdout: "ok", stderr: "", durationMs: 100 },
638
+ { command: "npm run test", exitCode: 0, stdout: "ok", stderr: "", durationMs: 200 },
639
639
  ],
640
640
  discoverySource: "preference",
641
641
  timestamp: Date.now(),
@@ -663,7 +663,6 @@ test("formatFailureContext: caps total output at 10,000 chars", () => {
663
663
  stdout: "",
664
664
  stderr: "e".repeat(1000), // 1000 chars each, 20 * ~1050 (with formatting) > 10,000
665
665
  durationMs: 100,
666
- blocking: true,
667
666
  });
668
667
  }
669
668
  const result: import("../types.ts").VerificationResult = {
@@ -1078,131 +1077,3 @@ test("dependency-audit: subdirectory package.json does not trigger audit", () =>
1078
1077
  assert.equal(npmAuditCalled, false, "subdirectory dependency files should not trigger audit");
1079
1078
  assert.deepStrictEqual(result, []);
1080
1079
  });
1081
-
1082
- // ─── Non-Blocking Discovery Tests ────────────────────────────────────────────
1083
-
1084
- test("non-blocking: package-json discovered commands failing → result.passed is still true", () => {
1085
- const tmp = makeTempDir("vg-nb-pkg-fail");
1086
- try {
1087
- writeFileSync(
1088
- join(tmp, "package.json"),
1089
- JSON.stringify({ scripts: { lint: "eslint .", test: "vitest" } }),
1090
- );
1091
- // These commands will fail because eslint/vitest don't exist in the temp dir
1092
- const result = runVerificationGate({
1093
- basePath: tmp,
1094
- unitId: "T01",
1095
- cwd: tmp,
1096
- // No preference commands — discovery falls through to package.json
1097
- });
1098
- assert.equal(result.discoverySource, "package-json");
1099
- assert.ok(result.checks.length > 0, "should have discovered package.json checks");
1100
- assert.equal(result.passed, true, "package-json failures should not block the gate");
1101
- for (const check of result.checks) {
1102
- assert.equal(check.blocking, false, "package-json checks should be non-blocking");
1103
- }
1104
- } finally {
1105
- rmSync(tmp, { recursive: true, force: true });
1106
- }
1107
- });
1108
-
1109
- test("non-blocking: preference commands failing → result.passed is false", () => {
1110
- const tmp = makeTempDir("vg-nb-pref-fail");
1111
- try {
1112
- const result = runVerificationGate({
1113
- basePath: tmp,
1114
- unitId: "T01",
1115
- cwd: tmp,
1116
- preferenceCommands: ["sh -c 'exit 1'"],
1117
- });
1118
- assert.equal(result.discoverySource, "preference");
1119
- assert.equal(result.passed, false, "preference failures should block the gate");
1120
- assert.equal(result.checks[0].blocking, true, "preference checks should be blocking");
1121
- } finally {
1122
- rmSync(tmp, { recursive: true, force: true });
1123
- }
1124
- });
1125
-
1126
- test("non-blocking: task-plan commands failing → result.passed is false", () => {
1127
- const tmp = makeTempDir("vg-nb-tp-fail");
1128
- try {
1129
- const result = runVerificationGate({
1130
- basePath: tmp,
1131
- unitId: "T01",
1132
- cwd: tmp,
1133
- taskPlanVerify: "sh -c 'exit 1'",
1134
- });
1135
- assert.equal(result.discoverySource, "task-plan");
1136
- assert.equal(result.passed, false, "task-plan failures should block the gate");
1137
- assert.equal(result.checks[0].blocking, true, "task-plan checks should be blocking");
1138
- } finally {
1139
- rmSync(tmp, { recursive: true, force: true });
1140
- }
1141
- });
1142
-
1143
- test("non-blocking: blocking field is set correctly based on discovery source", () => {
1144
- const tmp = makeTempDir("vg-nb-field");
1145
- try {
1146
- // preference → blocking
1147
- const prefResult = runVerificationGate({
1148
- basePath: tmp,
1149
- unitId: "T01",
1150
- cwd: tmp,
1151
- preferenceCommands: ["echo ok"],
1152
- });
1153
- assert.equal(prefResult.checks[0].blocking, true);
1154
-
1155
- // task-plan → blocking
1156
- const tpResult = runVerificationGate({
1157
- basePath: tmp,
1158
- unitId: "T01",
1159
- cwd: tmp,
1160
- taskPlanVerify: "echo ok",
1161
- });
1162
- assert.equal(tpResult.checks[0].blocking, true);
1163
-
1164
- // package-json → non-blocking
1165
- writeFileSync(
1166
- join(tmp, "package.json"),
1167
- JSON.stringify({ scripts: { test: "echo ok" } }),
1168
- );
1169
- const pkgResult = runVerificationGate({
1170
- basePath: tmp,
1171
- unitId: "T01",
1172
- cwd: tmp,
1173
- });
1174
- assert.equal(pkgResult.checks[0].blocking, false);
1175
- } finally {
1176
- rmSync(tmp, { recursive: true, force: true });
1177
- }
1178
- });
1179
-
1180
- test("non-blocking: formatFailureContext only includes blocking failures", () => {
1181
- const result: import("../types.ts").VerificationResult = {
1182
- passed: true,
1183
- checks: [
1184
- { command: "npm run lint", exitCode: 1, stdout: "", stderr: "lint warning", durationMs: 100, blocking: false },
1185
- { command: "npm run test", exitCode: 1, stdout: "", stderr: "test error", durationMs: 200, blocking: true },
1186
- { command: "npm run typecheck", exitCode: 1, stdout: "", stderr: "type error", durationMs: 50, blocking: false },
1187
- ],
1188
- discoverySource: "preference",
1189
- timestamp: Date.now(),
1190
- };
1191
- const output = formatFailureContext(result);
1192
- assert.ok(output.includes("`npm run test`"), "should include blocking failure");
1193
- assert.ok(!output.includes("npm run lint"), "should not include non-blocking failure");
1194
- assert.ok(!output.includes("npm run typecheck"), "should not include non-blocking failure");
1195
- });
1196
-
1197
- test("non-blocking: formatFailureContext returns empty when only non-blocking failures exist", () => {
1198
- const result: import("../types.ts").VerificationResult = {
1199
- passed: true,
1200
- checks: [
1201
- { command: "npm run lint", exitCode: 1, stdout: "", stderr: "lint warning", durationMs: 100, blocking: false },
1202
- { command: "npm run test", exitCode: 1, stdout: "", stderr: "test warning", durationMs: 200, blocking: false },
1203
- ],
1204
- discoverySource: "package-json",
1205
- timestamp: Date.now(),
1206
- };
1207
- assert.equal(formatFailureContext(result), "", "should return empty when only non-blocking failures");
1208
- });
@@ -55,7 +55,6 @@ export interface VerificationCheck {
55
55
  stdout: string;
56
56
  stderr: string;
57
57
  durationMs: number;
58
- blocking: boolean; // true for preference/task-plan sources, false for package-json (advisory only)
59
58
  }
60
59
 
61
60
  /** A runtime error captured from bg-shell processes or browser console */
@@ -20,7 +20,6 @@ export interface EvidenceCheckJSON {
20
20
  exitCode: number;
21
21
  durationMs: number;
22
22
  verdict: "pass" | "fail";
23
- blocking: boolean;
24
23
  }
25
24
 
26
25
  export interface RuntimeErrorJSON {
@@ -81,7 +80,6 @@ export function writeVerificationJSON(
81
80
  exitCode: check.exitCode,
82
81
  durationMs: check.durationMs,
83
82
  verdict: check.exitCode === 0 ? "pass" : "fail",
84
- blocking: check.blocking,
85
83
  })),
86
84
  ...(retryAttempt !== undefined ? { retryAttempt } : {}),
87
85
  ...(maxRetries !== undefined ? { maxRetries } : {}),
@@ -112,9 +112,7 @@ const MAX_FAILURE_CONTEXT_CHARS = 10_000;
112
112
  * Returns an empty string when all checks pass or the checks array is empty.
113
113
  */
114
114
  export function formatFailureContext(result: VerificationResult): string {
115
- // Only include blocking failures in retry context non-blocking (advisory) failures
116
- // should not be injected into retry prompts to avoid noise pollution.
117
- const failures = result.checks.filter((c) => c.exitCode !== 0 && c.blocking);
115
+ const failures = result.checks.filter((c) => c.exitCode !== 0);
118
116
  if (failures.length === 0) return "";
119
117
 
120
118
  const blocks: string[] = [];
@@ -258,10 +256,6 @@ export function runVerificationGate(options: RunVerificationGateOptions): Verifi
258
256
  };
259
257
  }
260
258
 
261
- // Commands from preference and task-plan sources are blocking;
262
- // package-json discovered commands are advisory (non-blocking).
263
- const blocking = source === "preference" || source === "task-plan";
264
-
265
259
  const checks: VerificationCheck[] = [];
266
260
 
267
261
  for (const command of commands) {
@@ -297,16 +291,11 @@ export function runVerificationGate(options: RunVerificationGateOptions): Verifi
297
291
  stdout: truncate(result.stdout, MAX_OUTPUT_BYTES),
298
292
  stderr,
299
293
  durationMs,
300
- blocking,
301
294
  });
302
295
  }
303
296
 
304
- // Gate passes if all blocking checks pass (non-blocking failures are advisory)
305
- const blockingChecks = checks.filter(c => c.blocking);
306
- const passed = blockingChecks.length === 0 || blockingChecks.every(c => c.exitCode === 0);
307
-
308
297
  return {
309
- passed,
298
+ passed: checks.every(c => c.exitCode === 0),
310
299
  checks,
311
300
  discoverySource: source,
312
301
  timestamp,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gsd-pi",
3
- "version": "2.28.0-dev.704ded6",
3
+ "version": "2.28.0-dev.853dfc5",
4
4
  "description": "GSD — Get Shit Done coding agent",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,55 +1,24 @@
1
1
  #!/usr/bin/env node
2
- const { mkdirSync, cpSync, copyFileSync, readdirSync } = require('fs');
3
- const { join } = require('path');
4
-
5
- /**
6
- * Recursive directory copy using copyFileSync — workaround for cpSync failures
7
- * on Windows paths containing non-ASCII characters (#1178).
8
- */
9
- function safeCpSync(src, dest, options) {
10
- try {
11
- cpSync(src, dest, options);
12
- } catch {
13
- if (options && options.recursive) {
14
- copyDirRecursive(src, dest, options && options.filter);
15
- } else {
16
- copyFileSync(src, dest);
17
- }
18
- }
19
- }
20
-
21
- function copyDirRecursive(src, dest, filter) {
22
- mkdirSync(dest, { recursive: true });
23
- for (const entry of readdirSync(src, { withFileTypes: true })) {
24
- const srcPath = join(src, entry.name);
25
- const destPath = join(dest, entry.name);
26
- if (filter && !filter(srcPath)) continue;
27
- if (entry.isDirectory()) {
28
- copyDirRecursive(srcPath, destPath, filter);
29
- } else {
30
- copyFileSync(srcPath, destPath);
31
- }
32
- }
33
- }
2
+ const { mkdirSync, cpSync } = require('fs');
34
3
 
35
4
  // Theme assets
36
5
  mkdirSync('dist/modes/interactive/theme', { recursive: true });
37
- safeCpSync('src/modes/interactive/theme', 'dist/modes/interactive/theme', {
6
+ cpSync('src/modes/interactive/theme', 'dist/modes/interactive/theme', {
38
7
  recursive: true,
39
8
  filter: (s) => !s.endsWith('.ts'),
40
9
  });
41
10
 
42
11
  // Export HTML templates and vendor files
43
12
  mkdirSync('dist/core/export-html/vendor', { recursive: true });
44
- safeCpSync('src/core/export-html/template.html', 'dist/core/export-html/template.html');
45
- safeCpSync('src/core/export-html/template.css', 'dist/core/export-html/template.css');
46
- safeCpSync('src/core/export-html/template.js', 'dist/core/export-html/template.js');
47
- safeCpSync('src/core/export-html/vendor', 'dist/core/export-html/vendor', {
13
+ cpSync('src/core/export-html/template.html', 'dist/core/export-html/template.html');
14
+ cpSync('src/core/export-html/template.css', 'dist/core/export-html/template.css');
15
+ cpSync('src/core/export-html/template.js', 'dist/core/export-html/template.js');
16
+ cpSync('src/core/export-html/vendor', 'dist/core/export-html/vendor', {
48
17
  recursive: true,
49
18
  filter: (s) => !s.endsWith('.ts'),
50
19
  });
51
20
 
52
21
  // LSP defaults
53
22
  mkdirSync('dist/core/lsp', { recursive: true });
54
- safeCpSync('src/core/lsp/defaults.json', 'dist/core/lsp/defaults.json');
55
- safeCpSync('src/core/lsp/lsp.md', 'dist/core/lsp/lsp.md');
23
+ cpSync('src/core/lsp/defaults.json', 'dist/core/lsp/defaults.json');
24
+ cpSync('src/core/lsp/lsp.md', 'dist/core/lsp/lsp.md');
@@ -105,39 +105,19 @@ export async function runPostUnitVerification(
105
105
  const completionKey = `${s.currentUnit.type}/${s.currentUnit.id}`;
106
106
 
107
107
  if (result.checks.length > 0) {
108
- const blockingChecks = result.checks.filter(c => c.blocking);
109
- const advisoryChecks = result.checks.filter(c => !c.blocking);
110
- const blockingPassCount = blockingChecks.filter(c => c.exitCode === 0).length;
111
- const advisoryFailCount = advisoryChecks.filter(c => c.exitCode !== 0).length;
112
-
108
+ const passCount = result.checks.filter(c => c.exitCode === 0).length;
109
+ const total = result.checks.length;
113
110
  if (result.passed) {
114
- let msg = blockingChecks.length > 0
115
- ? `Verification gate: ${blockingPassCount}/${blockingChecks.length} blocking checks passed`
116
- : `Verification gate: passed (no blocking checks)`;
117
- if (advisoryFailCount > 0) {
118
- msg += ` (${advisoryFailCount} advisory warning${advisoryFailCount > 1 ? "s" : ""})`;
119
- }
120
- ctx.ui.notify(msg);
121
- // Log advisory warnings to stderr for visibility
122
- if (advisoryFailCount > 0) {
123
- const advisoryFailures = advisoryChecks.filter(c => c.exitCode !== 0);
124
- process.stderr.write(`verification-gate: ${advisoryFailCount} advisory (non-blocking) failure(s)\n`);
125
- for (const f of advisoryFailures) {
126
- process.stderr.write(` [advisory] ${f.command} exited ${f.exitCode}\n`);
127
- }
128
- }
111
+ ctx.ui.notify(`Verification gate: ${passCount}/${total} checks passed`);
129
112
  } else {
130
- const blockingFailures = blockingChecks.filter(c => c.exitCode !== 0);
131
- const failNames = blockingFailures.map(f => f.command).join(", ");
113
+ const failures = result.checks.filter(c => c.exitCode !== 0);
114
+ const failNames = failures.map(f => f.command).join(", ");
132
115
  ctx.ui.notify(`Verification gate: FAILED — ${failNames}`);
133
- process.stderr.write(`verification-gate: ${blockingFailures.length}/${blockingChecks.length} blocking checks failed\n`);
134
- for (const f of blockingFailures) {
116
+ process.stderr.write(`verification-gate: ${total - passCount}/${total} checks failed\n`);
117
+ for (const f of failures) {
135
118
  process.stderr.write(` ${f.command} exited ${f.exitCode}\n`);
136
119
  if (f.stderr) process.stderr.write(` stderr: ${f.stderr.slice(0, 500)}\n`);
137
120
  }
138
- if (advisoryFailCount > 0) {
139
- process.stderr.write(`verification-gate: ${advisoryFailCount} additional advisory (non-blocking) failure(s)\n`);
140
- }
141
121
  }
142
122
  }
143
123
 
@@ -58,7 +58,6 @@ test("verification-evidence: writeVerificationJSON writes correct JSON shape", (
58
58
  stdout: "all good",
59
59
  stderr: "",
60
60
  durationMs: 2340,
61
- blocking: true,
62
61
  },
63
62
  ],
64
63
  });
@@ -106,9 +105,9 @@ test("verification-evidence: writeVerificationJSON maps exitCode to verdict corr
106
105
  const result = makeResult({
107
106
  passed: false,
108
107
  checks: [
109
- { command: "lint", exitCode: 0, stdout: "", stderr: "", durationMs: 100, blocking: true },
110
- { command: "test", exitCode: 1, stdout: "", stderr: "fail", durationMs: 200, blocking: true },
111
- { command: "audit", exitCode: 2, stdout: "", stderr: "err", durationMs: 300, blocking: true },
108
+ { command: "lint", exitCode: 0, stdout: "", stderr: "", durationMs: 100 },
109
+ { command: "test", exitCode: 1, stdout: "", stderr: "fail", durationMs: 200 },
110
+ { command: "audit", exitCode: 2, stdout: "", stderr: "err", durationMs: 300 },
112
111
  ],
113
112
  });
114
113
 
@@ -134,7 +133,6 @@ test("verification-evidence: writeVerificationJSON excludes stdout/stderr from o
134
133
  stdout: "hello\n",
135
134
  stderr: "some warning",
136
135
  durationMs: 50,
137
- blocking: true,
138
136
  },
139
137
  ],
140
138
  });
@@ -183,8 +181,8 @@ test("verification-evidence: writeVerificationJSON uses optional unitId when pro
183
181
  test("verification-evidence: formatEvidenceTable returns markdown table with correct columns", () => {
184
182
  const result = makeResult({
185
183
  checks: [
186
- { command: "npm run typecheck", exitCode: 0, stdout: "", stderr: "", durationMs: 2340, blocking: true },
187
- { command: "npm run lint", exitCode: 1, stdout: "", stderr: "err", durationMs: 1100, blocking: true },
184
+ { command: "npm run typecheck", exitCode: 0, stdout: "", stderr: "", durationMs: 2340 },
185
+ { command: "npm run lint", exitCode: 1, stdout: "", stderr: "err", durationMs: 1100 },
188
186
  ],
189
187
  });
190
188
 
@@ -216,9 +214,9 @@ test("verification-evidence: formatEvidenceTable returns no-checks message for e
216
214
  test("verification-evidence: formatEvidenceTable formats duration as seconds with 1 decimal", () => {
217
215
  const result = makeResult({
218
216
  checks: [
219
- { command: "fast", exitCode: 0, stdout: "", stderr: "", durationMs: 150, blocking: true },
220
- { command: "slow", exitCode: 0, stdout: "", stderr: "", durationMs: 2340, blocking: true },
221
- { command: "zero", exitCode: 0, stdout: "", stderr: "", durationMs: 0, blocking: true },
217
+ { command: "fast", exitCode: 0, stdout: "", stderr: "", durationMs: 150 },
218
+ { command: "slow", exitCode: 0, stdout: "", stderr: "", durationMs: 2340 },
219
+ { command: "zero", exitCode: 0, stdout: "", stderr: "", durationMs: 0 },
222
220
  ],
223
221
  });
224
222
 
@@ -232,8 +230,8 @@ test("verification-evidence: formatEvidenceTable uses ✅/❌ emoji for pass/fai
232
230
  const result = makeResult({
233
231
  passed: false,
234
232
  checks: [
235
- { command: "pass-cmd", exitCode: 0, stdout: "", stderr: "", durationMs: 100, blocking: true },
236
- { command: "fail-cmd", exitCode: 1, stdout: "", stderr: "", durationMs: 200, blocking: true },
233
+ { command: "pass-cmd", exitCode: 0, stdout: "", stderr: "", durationMs: 100 },
234
+ { command: "fail-cmd", exitCode: 1, stdout: "", stderr: "", durationMs: 200 },
237
235
  ],
238
236
  });
239
237
 
@@ -337,8 +335,8 @@ test("verification-evidence: integration — VerificationResult → JSON → tab
337
335
  const result = makeResult({
338
336
  passed: false,
339
337
  checks: [
340
- { command: "npm run typecheck", exitCode: 0, stdout: "ok", stderr: "", durationMs: 1500, blocking: true },
341
- { command: "npm run test:unit", exitCode: 1, stdout: "", stderr: "1 failed", durationMs: 3200, blocking: true },
338
+ { command: "npm run typecheck", exitCode: 0, stdout: "ok", stderr: "", durationMs: 1500 },
339
+ { command: "npm run test:unit", exitCode: 1, stdout: "", stderr: "1 failed", durationMs: 3200 },
342
340
  ],
343
341
  discoverySource: "package-json",
344
342
  });
@@ -392,7 +390,7 @@ test("verification-evidence: writeVerificationJSON with retryAttempt and maxRetr
392
390
  const result = makeResult({
393
391
  passed: false,
394
392
  checks: [
395
- { command: "npm run lint", exitCode: 1, stdout: "", stderr: "error", durationMs: 300, blocking: true },
393
+ { command: "npm run lint", exitCode: 1, stdout: "", stderr: "error", durationMs: 300 },
396
394
  ],
397
395
  });
398
396
 
@@ -417,7 +415,7 @@ test("verification-evidence: writeVerificationJSON without retry params omits re
417
415
  const result = makeResult({
418
416
  passed: true,
419
417
  checks: [
420
- { command: "npm run test", exitCode: 0, stdout: "ok", stderr: "", durationMs: 100, blocking: true },
418
+ { command: "npm run test", exitCode: 0, stdout: "ok", stderr: "", durationMs: 100 },
421
419
  ],
422
420
  });
423
421
 
@@ -443,7 +441,7 @@ test("verification-evidence: writeVerificationJSON includes runtimeErrors when p
443
441
  const result = makeResult({
444
442
  passed: false,
445
443
  checks: [
446
- { command: "npm run test", exitCode: 0, stdout: "ok", stderr: "", durationMs: 100, blocking: true },
444
+ { command: "npm run test", exitCode: 0, stdout: "ok", stderr: "", durationMs: 100 },
447
445
  ],
448
446
  runtimeErrors: [
449
447
  { source: "bg-shell", severity: "crash", message: "Server crashed", blocking: true },
@@ -475,7 +473,7 @@ test("verification-evidence: writeVerificationJSON omits runtimeErrors when abse
475
473
  const result = makeResult({
476
474
  passed: true,
477
475
  checks: [
478
- { command: "npm run lint", exitCode: 0, stdout: "", stderr: "", durationMs: 50, blocking: true },
476
+ { command: "npm run lint", exitCode: 0, stdout: "", stderr: "", durationMs: 50 },
479
477
  ],
480
478
  });
481
479
 
@@ -514,7 +512,7 @@ test("verification-evidence: formatEvidenceTable appends runtime errors section"
514
512
  const result = makeResult({
515
513
  passed: false,
516
514
  checks: [
517
- { command: "npm run test", exitCode: 0, stdout: "", stderr: "", durationMs: 100, blocking: true },
515
+ { command: "npm run test", exitCode: 0, stdout: "", stderr: "", durationMs: 100 },
518
516
  ],
519
517
  runtimeErrors: [
520
518
  { source: "bg-shell", severity: "crash", message: "Server crashed with SIGKILL", blocking: true },
@@ -539,7 +537,7 @@ test("verification-evidence: formatEvidenceTable omits runtime errors section wh
539
537
  const result = makeResult({
540
538
  passed: true,
541
539
  checks: [
542
- { command: "npm run lint", exitCode: 0, stdout: "", stderr: "", durationMs: 200, blocking: true },
540
+ { command: "npm run lint", exitCode: 0, stdout: "", stderr: "", durationMs: 200 },
543
541
  ],
544
542
  });
545
543
 
@@ -554,7 +552,7 @@ test("verification-evidence: formatEvidenceTable truncates runtime error message
554
552
  const result = makeResult({
555
553
  passed: false,
556
554
  checks: [
557
- { command: "npm run test", exitCode: 0, stdout: "", stderr: "", durationMs: 100, blocking: true },
555
+ { command: "npm run test", exitCode: 0, stdout: "", stderr: "", durationMs: 100 },
558
556
  ],
559
557
  runtimeErrors: [
560
558
  { source: "bg-shell", severity: "error", message: longMessage, blocking: false },
@@ -600,7 +598,7 @@ test("verification-evidence: writeVerificationJSON includes auditWarnings when p
600
598
  const result = makeResult({
601
599
  passed: true,
602
600
  checks: [
603
- { command: "npm run test", exitCode: 0, stdout: "ok", stderr: "", durationMs: 100, blocking: true },
601
+ { command: "npm run test", exitCode: 0, stdout: "ok", stderr: "", durationMs: 100 },
604
602
  ],
605
603
  auditWarnings: SAMPLE_AUDIT_WARNINGS,
606
604
  });
@@ -629,7 +627,7 @@ test("verification-evidence: writeVerificationJSON omits auditWarnings when abse
629
627
  const result = makeResult({
630
628
  passed: true,
631
629
  checks: [
632
- { command: "npm run lint", exitCode: 0, stdout: "", stderr: "", durationMs: 50, blocking: true },
630
+ { command: "npm run lint", exitCode: 0, stdout: "", stderr: "", durationMs: 50 },
633
631
  ],
634
632
  });
635
633
 
@@ -668,7 +666,7 @@ test("verification-evidence: formatEvidenceTable appends audit warnings section"
668
666
  const result = makeResult({
669
667
  passed: true,
670
668
  checks: [
671
- { command: "npm run test", exitCode: 0, stdout: "", stderr: "", durationMs: 100, blocking: true },
669
+ { command: "npm run test", exitCode: 0, stdout: "", stderr: "", durationMs: 100 },
672
670
  ],
673
671
  auditWarnings: SAMPLE_AUDIT_WARNINGS,
674
672
  });
@@ -691,7 +689,7 @@ test("verification-evidence: formatEvidenceTable omits audit warnings section wh
691
689
  const result = makeResult({
692
690
  passed: true,
693
691
  checks: [
694
- { command: "npm run lint", exitCode: 0, stdout: "", stderr: "", durationMs: 200, blocking: true },
692
+ { command: "npm run lint", exitCode: 0, stdout: "", stderr: "", durationMs: 200 },
695
693
  ],
696
694
  });
697
695
 
@@ -707,7 +705,7 @@ test("verification-evidence: integration — VerificationResult with auditWarnin
707
705
  const result = makeResult({
708
706
  passed: true,
709
707
  checks: [
710
- { command: "npm run typecheck", exitCode: 0, stdout: "ok", stderr: "", durationMs: 1500, blocking: true },
708
+ { command: "npm run typecheck", exitCode: 0, stdout: "ok", stderr: "", durationMs: 1500 },
711
709
  ],
712
710
  auditWarnings: [
713
711
  {
@@ -581,7 +581,7 @@ test("formatFailureContext: formats a single failure with command, exit code, st
581
581
  const result: import("../types.ts").VerificationResult = {
582
582
  passed: false,
583
583
  checks: [
584
- { command: "npm run lint", exitCode: 1, stdout: "", stderr: "error: unused var", durationMs: 500, blocking: true },
584
+ { command: "npm run lint", exitCode: 1, stdout: "", stderr: "error: unused var", durationMs: 500 },
585
585
  ],
586
586
  discoverySource: "preference",
587
587
  timestamp: Date.now(),
@@ -598,9 +598,9 @@ test("formatFailureContext: formats multiple failures", () => {
598
598
  const result: import("../types.ts").VerificationResult = {
599
599
  passed: false,
600
600
  checks: [
601
- { command: "npm run lint", exitCode: 1, stdout: "", stderr: "lint error", durationMs: 100, blocking: true },
602
- { command: "npm run test", exitCode: 2, stdout: "", stderr: "test failure", durationMs: 200, blocking: true },
603
- { command: "npm run typecheck", exitCode: 0, stdout: "ok", stderr: "", durationMs: 50, blocking: true },
601
+ { command: "npm run lint", exitCode: 1, stdout: "", stderr: "lint error", durationMs: 100 },
602
+ { command: "npm run test", exitCode: 2, stdout: "", stderr: "test failure", durationMs: 200 },
603
+ { command: "npm run typecheck", exitCode: 0, stdout: "ok", stderr: "", durationMs: 50 },
604
604
  ],
605
605
  discoverySource: "preference",
606
606
  timestamp: Date.now(),
@@ -619,7 +619,7 @@ test("formatFailureContext: truncates stderr longer than 2000 chars", () => {
619
619
  const result: import("../types.ts").VerificationResult = {
620
620
  passed: false,
621
621
  checks: [
622
- { command: "big-err", exitCode: 1, stdout: "", stderr: longStderr, durationMs: 100, blocking: true },
622
+ { command: "big-err", exitCode: 1, stdout: "", stderr: longStderr, durationMs: 100 },
623
623
  ],
624
624
  discoverySource: "preference",
625
625
  timestamp: Date.now(),
@@ -634,8 +634,8 @@ test("formatFailureContext: returns empty string when all checks pass", () => {
634
634
  const result: import("../types.ts").VerificationResult = {
635
635
  passed: true,
636
636
  checks: [
637
- { command: "npm run lint", exitCode: 0, stdout: "ok", stderr: "", durationMs: 100, blocking: true },
638
- { command: "npm run test", exitCode: 0, stdout: "ok", stderr: "", durationMs: 200, blocking: true },
637
+ { command: "npm run lint", exitCode: 0, stdout: "ok", stderr: "", durationMs: 100 },
638
+ { command: "npm run test", exitCode: 0, stdout: "ok", stderr: "", durationMs: 200 },
639
639
  ],
640
640
  discoverySource: "preference",
641
641
  timestamp: Date.now(),
@@ -663,7 +663,6 @@ test("formatFailureContext: caps total output at 10,000 chars", () => {
663
663
  stdout: "",
664
664
  stderr: "e".repeat(1000), // 1000 chars each, 20 * ~1050 (with formatting) > 10,000
665
665
  durationMs: 100,
666
- blocking: true,
667
666
  });
668
667
  }
669
668
  const result: import("../types.ts").VerificationResult = {
@@ -1078,131 +1077,3 @@ test("dependency-audit: subdirectory package.json does not trigger audit", () =>
1078
1077
  assert.equal(npmAuditCalled, false, "subdirectory dependency files should not trigger audit");
1079
1078
  assert.deepStrictEqual(result, []);
1080
1079
  });
1081
-
1082
- // ─── Non-Blocking Discovery Tests ────────────────────────────────────────────
1083
-
1084
- test("non-blocking: package-json discovered commands failing → result.passed is still true", () => {
1085
- const tmp = makeTempDir("vg-nb-pkg-fail");
1086
- try {
1087
- writeFileSync(
1088
- join(tmp, "package.json"),
1089
- JSON.stringify({ scripts: { lint: "eslint .", test: "vitest" } }),
1090
- );
1091
- // These commands will fail because eslint/vitest don't exist in the temp dir
1092
- const result = runVerificationGate({
1093
- basePath: tmp,
1094
- unitId: "T01",
1095
- cwd: tmp,
1096
- // No preference commands — discovery falls through to package.json
1097
- });
1098
- assert.equal(result.discoverySource, "package-json");
1099
- assert.ok(result.checks.length > 0, "should have discovered package.json checks");
1100
- assert.equal(result.passed, true, "package-json failures should not block the gate");
1101
- for (const check of result.checks) {
1102
- assert.equal(check.blocking, false, "package-json checks should be non-blocking");
1103
- }
1104
- } finally {
1105
- rmSync(tmp, { recursive: true, force: true });
1106
- }
1107
- });
1108
-
1109
- test("non-blocking: preference commands failing → result.passed is false", () => {
1110
- const tmp = makeTempDir("vg-nb-pref-fail");
1111
- try {
1112
- const result = runVerificationGate({
1113
- basePath: tmp,
1114
- unitId: "T01",
1115
- cwd: tmp,
1116
- preferenceCommands: ["sh -c 'exit 1'"],
1117
- });
1118
- assert.equal(result.discoverySource, "preference");
1119
- assert.equal(result.passed, false, "preference failures should block the gate");
1120
- assert.equal(result.checks[0].blocking, true, "preference checks should be blocking");
1121
- } finally {
1122
- rmSync(tmp, { recursive: true, force: true });
1123
- }
1124
- });
1125
-
1126
- test("non-blocking: task-plan commands failing → result.passed is false", () => {
1127
- const tmp = makeTempDir("vg-nb-tp-fail");
1128
- try {
1129
- const result = runVerificationGate({
1130
- basePath: tmp,
1131
- unitId: "T01",
1132
- cwd: tmp,
1133
- taskPlanVerify: "sh -c 'exit 1'",
1134
- });
1135
- assert.equal(result.discoverySource, "task-plan");
1136
- assert.equal(result.passed, false, "task-plan failures should block the gate");
1137
- assert.equal(result.checks[0].blocking, true, "task-plan checks should be blocking");
1138
- } finally {
1139
- rmSync(tmp, { recursive: true, force: true });
1140
- }
1141
- });
1142
-
1143
- test("non-blocking: blocking field is set correctly based on discovery source", () => {
1144
- const tmp = makeTempDir("vg-nb-field");
1145
- try {
1146
- // preference → blocking
1147
- const prefResult = runVerificationGate({
1148
- basePath: tmp,
1149
- unitId: "T01",
1150
- cwd: tmp,
1151
- preferenceCommands: ["echo ok"],
1152
- });
1153
- assert.equal(prefResult.checks[0].blocking, true);
1154
-
1155
- // task-plan → blocking
1156
- const tpResult = runVerificationGate({
1157
- basePath: tmp,
1158
- unitId: "T01",
1159
- cwd: tmp,
1160
- taskPlanVerify: "echo ok",
1161
- });
1162
- assert.equal(tpResult.checks[0].blocking, true);
1163
-
1164
- // package-json → non-blocking
1165
- writeFileSync(
1166
- join(tmp, "package.json"),
1167
- JSON.stringify({ scripts: { test: "echo ok" } }),
1168
- );
1169
- const pkgResult = runVerificationGate({
1170
- basePath: tmp,
1171
- unitId: "T01",
1172
- cwd: tmp,
1173
- });
1174
- assert.equal(pkgResult.checks[0].blocking, false);
1175
- } finally {
1176
- rmSync(tmp, { recursive: true, force: true });
1177
- }
1178
- });
1179
-
1180
- test("non-blocking: formatFailureContext only includes blocking failures", () => {
1181
- const result: import("../types.ts").VerificationResult = {
1182
- passed: true,
1183
- checks: [
1184
- { command: "npm run lint", exitCode: 1, stdout: "", stderr: "lint warning", durationMs: 100, blocking: false },
1185
- { command: "npm run test", exitCode: 1, stdout: "", stderr: "test error", durationMs: 200, blocking: true },
1186
- { command: "npm run typecheck", exitCode: 1, stdout: "", stderr: "type error", durationMs: 50, blocking: false },
1187
- ],
1188
- discoverySource: "preference",
1189
- timestamp: Date.now(),
1190
- };
1191
- const output = formatFailureContext(result);
1192
- assert.ok(output.includes("`npm run test`"), "should include blocking failure");
1193
- assert.ok(!output.includes("npm run lint"), "should not include non-blocking failure");
1194
- assert.ok(!output.includes("npm run typecheck"), "should not include non-blocking failure");
1195
- });
1196
-
1197
- test("non-blocking: formatFailureContext returns empty when only non-blocking failures exist", () => {
1198
- const result: import("../types.ts").VerificationResult = {
1199
- passed: true,
1200
- checks: [
1201
- { command: "npm run lint", exitCode: 1, stdout: "", stderr: "lint warning", durationMs: 100, blocking: false },
1202
- { command: "npm run test", exitCode: 1, stdout: "", stderr: "test warning", durationMs: 200, blocking: false },
1203
- ],
1204
- discoverySource: "package-json",
1205
- timestamp: Date.now(),
1206
- };
1207
- assert.equal(formatFailureContext(result), "", "should return empty when only non-blocking failures");
1208
- });
@@ -55,7 +55,6 @@ export interface VerificationCheck {
55
55
  stdout: string;
56
56
  stderr: string;
57
57
  durationMs: number;
58
- blocking: boolean; // true for preference/task-plan sources, false for package-json (advisory only)
59
58
  }
60
59
 
61
60
  /** A runtime error captured from bg-shell processes or browser console */
@@ -20,7 +20,6 @@ export interface EvidenceCheckJSON {
20
20
  exitCode: number;
21
21
  durationMs: number;
22
22
  verdict: "pass" | "fail";
23
- blocking: boolean;
24
23
  }
25
24
 
26
25
  export interface RuntimeErrorJSON {
@@ -81,7 +80,6 @@ export function writeVerificationJSON(
81
80
  exitCode: check.exitCode,
82
81
  durationMs: check.durationMs,
83
82
  verdict: check.exitCode === 0 ? "pass" : "fail",
84
- blocking: check.blocking,
85
83
  })),
86
84
  ...(retryAttempt !== undefined ? { retryAttempt } : {}),
87
85
  ...(maxRetries !== undefined ? { maxRetries } : {}),
@@ -112,9 +112,7 @@ const MAX_FAILURE_CONTEXT_CHARS = 10_000;
112
112
  * Returns an empty string when all checks pass or the checks array is empty.
113
113
  */
114
114
  export function formatFailureContext(result: VerificationResult): string {
115
- // Only include blocking failures in retry context non-blocking (advisory) failures
116
- // should not be injected into retry prompts to avoid noise pollution.
117
- const failures = result.checks.filter((c) => c.exitCode !== 0 && c.blocking);
115
+ const failures = result.checks.filter((c) => c.exitCode !== 0);
118
116
  if (failures.length === 0) return "";
119
117
 
120
118
  const blocks: string[] = [];
@@ -258,10 +256,6 @@ export function runVerificationGate(options: RunVerificationGateOptions): Verifi
258
256
  };
259
257
  }
260
258
 
261
- // Commands from preference and task-plan sources are blocking;
262
- // package-json discovered commands are advisory (non-blocking).
263
- const blocking = source === "preference" || source === "task-plan";
264
-
265
259
  const checks: VerificationCheck[] = [];
266
260
 
267
261
  for (const command of commands) {
@@ -297,16 +291,11 @@ export function runVerificationGate(options: RunVerificationGateOptions): Verifi
297
291
  stdout: truncate(result.stdout, MAX_OUTPUT_BYTES),
298
292
  stderr,
299
293
  durationMs,
300
- blocking,
301
294
  });
302
295
  }
303
296
 
304
- // Gate passes if all blocking checks pass (non-blocking failures are advisory)
305
- const blockingChecks = checks.filter(c => c.blocking);
306
- const passed = blockingChecks.length === 0 || blockingChecks.every(c => c.exitCode === 0);
307
-
308
297
  return {
309
- passed,
298
+ passed: checks.every(c => c.exitCode === 0),
310
299
  checks,
311
300
  discoverySource: source,
312
301
  timestamp,