@mjasnikovs/pi-task 0.21.2 → 0.21.4
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/task/accept-debt.d.ts +5 -0
- package/dist/task/auto-orchestrator.d.ts +2 -0
- package/dist/task/auto-orchestrator.js +35 -4
- package/dist/task/final-gate-fix.d.ts +6 -0
- package/dist/task/final-gate-fix.js +1 -1
- package/dist/task/final-gate.d.ts +70 -0
- package/dist/task/final-gate.js +93 -4
- package/package.json +1 -1
|
@@ -29,6 +29,11 @@
|
|
|
29
29
|
* `lsof` and the sandbox had neither) and no further attempt can move it. The
|
|
30
30
|
* run is allowed to converge on the REMAINING checks, carrying this one here
|
|
31
31
|
* so the next run's gate re-checks and re-surfaces it rather than losing it.
|
|
32
|
+
* Also recorded for the ZERO-OBSERVATION verdict (final-gate.ts
|
|
33
|
+
* unobservedVerdict): the whole gate observed nothing dynamic — usually because
|
|
34
|
+
* the project's ecosystem exposes no discoverable command at all — so the run
|
|
35
|
+
* completed on statics alone. Same treatment for the same reason: unprovable
|
|
36
|
+
* here, so carry it forward rather than let a silent PASS bury it.
|
|
32
37
|
* - 'root-cause' — the task's verify FAILed because of a PRE-EXISTING defect in a
|
|
33
38
|
* file a DIFFERENT task created, which this task's own work never touched (mx5
|
|
34
39
|
* run 14: TASK_0007's `test/teardown.ts` TRUNCATE bug FAILed TASK_0013 and
|
|
@@ -36,6 +36,8 @@ export interface AutoDeps extends GateDeps {
|
|
|
36
36
|
failures?: string[];
|
|
37
37
|
debtNote?: string;
|
|
38
38
|
openDebts?: AcceptDebt[];
|
|
39
|
+
/** Set ⇒ the gate observed nothing dynamic: UNOBSERVED, not PASS. */
|
|
40
|
+
unobserved?: string;
|
|
39
41
|
}>;
|
|
40
42
|
/**
|
|
41
43
|
* Bounded model-driven fix pass for a final-gate FAIL (see final-gate-fix.ts),
|
|
@@ -1063,6 +1063,11 @@ export async function runAutoLoop(ctx, cwd, id, deps) {
|
|
|
1063
1063
|
announceDone(active, `${id} cancelled before the final integration gate — resume with /task-auto-resume.`, 'warning');
|
|
1064
1064
|
return;
|
|
1065
1065
|
}
|
|
1066
|
+
// Set when the gate finished having observed NOTHING dynamic. Declared
|
|
1067
|
+
// out here so the run-completion announcement below can say so: a run
|
|
1068
|
+
// that completes on statics alone must not be announced the same way as
|
|
1069
|
+
// one whose product was actually exercised.
|
|
1070
|
+
let unobservedNote = null;
|
|
1066
1071
|
if (deps.finalGate) {
|
|
1067
1072
|
active.ui.notify(`${id}: running final integration gate…`, 'info');
|
|
1068
1073
|
// Run-level gate trail on the parent task file — same durable
|
|
@@ -1096,7 +1101,21 @@ export async function runAutoLoop(ctx, cwd, id, deps) {
|
|
|
1096
1101
|
// Record the outcome symmetrically (mx5 run 10 item 7): only FAIL was
|
|
1097
1102
|
// ever trailed, so a PASSing gate was indistinguishable from a gate
|
|
1098
1103
|
// that never ran. The PASS reason names the commands that were run.
|
|
1099
|
-
|
|
1104
|
+
// THREE verdicts, not two (final-gate.ts unobservedVerdict): a gate
|
|
1105
|
+
// that observed nothing dynamic is UNOBSERVED, never PASS — IAR1
|
|
1106
|
+
// shipped `PASS — no integration command found` twice while carrying
|
|
1107
|
+
// open verify-FAIL debt. It does not block (justified there), but it
|
|
1108
|
+
// is labelled here, warned about, and recorded as durable debt so the
|
|
1109
|
+
// next run's gate re-surfaces it.
|
|
1110
|
+
if (fin.ok && fin.unobserved) {
|
|
1111
|
+
unobservedNote = fin.unobserved;
|
|
1112
|
+
await recGate(`final-gate: UNOBSERVED — ${fin.reason.slice(0, 300)}`);
|
|
1113
|
+
await recordFinalGateUnobservedDebt(cwd, id, fin.unobserved);
|
|
1114
|
+
active.ui.notify(`${id}: the final integration gate observed NOTHING dynamic — `
|
|
1115
|
+
+ 'the run completed on static checks alone. Nothing verified '
|
|
1116
|
+
+ 'that the assembled product builds, boots or works.', 'warning');
|
|
1117
|
+
}
|
|
1118
|
+
else if (fin.ok) {
|
|
1100
1119
|
await recGate(`final-gate: PASS — ${fin.reason.slice(0, 300)}`);
|
|
1101
1120
|
}
|
|
1102
1121
|
else {
|
|
@@ -1247,8 +1266,15 @@ export async function runAutoLoop(ctx, cwd, id, deps) {
|
|
|
1247
1266
|
const fix = await deps.finalGateFix(active, cwd, seed);
|
|
1248
1267
|
if (fix.ok) {
|
|
1249
1268
|
await deps.commit(cwd, `FINAL GATE AUTOFIX (${id})`);
|
|
1250
|
-
|
|
1251
|
-
|
|
1269
|
+
// A converged re-run that observed nothing dynamic is
|
|
1270
|
+
// UNOBSERVED on this door too — never announce it as a
|
|
1271
|
+
// PASS just because it arrived via autofix.
|
|
1272
|
+
if (fix.unobserved) {
|
|
1273
|
+
unobservedNote = fix.unobserved;
|
|
1274
|
+
await recordFinalGateUnobservedDebt(cwd, id, fix.unobserved);
|
|
1275
|
+
}
|
|
1276
|
+
await recGate(`final-gate: autofix ${fix.unobserved ? 'ended UNOBSERVED' : 'converged'} — ${fix.reason.slice(0, 200)}`);
|
|
1277
|
+
active.ui.notify(`${id}: final integration gate ${fix.unobserved ? 'is UNOBSERVED' : 'PASSES'} after autofix — ${fix.reason.slice(0, 140)}`, fix.unobserved ? 'warning' : 'info');
|
|
1252
1278
|
fin = { ok: true, reason: fix.reason };
|
|
1253
1279
|
break;
|
|
1254
1280
|
}
|
|
@@ -1367,7 +1393,12 @@ export async function runAutoLoop(ctx, cwd, id, deps) {
|
|
|
1367
1393
|
}
|
|
1368
1394
|
}
|
|
1369
1395
|
await updateTaskFrontMatter(cwd, id, { state: 'completed' });
|
|
1370
|
-
announceDone(active, `${id} complete — all ${entries.length} tasks done
|
|
1396
|
+
announceDone(active, `${id} complete — all ${entries.length} tasks done.`
|
|
1397
|
+
+ (unobservedNote ?
|
|
1398
|
+
' WARNING: the final integration gate was UNOBSERVED — it ran no '
|
|
1399
|
+
+ 'dynamic check at all, so "complete" here means the statics '
|
|
1400
|
+
+ 'passed and nothing more. Carried as debt for the next run.'
|
|
1401
|
+
: ''), unobservedNote ? 'warning' : 'info');
|
|
1371
1402
|
return;
|
|
1372
1403
|
}
|
|
1373
1404
|
// REFUSE to start on a conflicted tree: an unmerged index dooms every
|
|
@@ -103,6 +103,11 @@ export interface FinalFixResult {
|
|
|
103
103
|
/** The fresh gate's individual ranked failures (see FinalGateOutcome.failures),
|
|
104
104
|
* so the caller can trail each entry — never just the first. */
|
|
105
105
|
gateFailures?: string[];
|
|
106
|
+
/** On a converged outcome: the re-run gate's UNOBSERVED note, if it observed
|
|
107
|
+
* nothing dynamic (see FinalGateOutcome.unobserved). Carried so the caller
|
|
108
|
+
* labels a converge-on-statics-alone the same way it labels a first-pass one —
|
|
109
|
+
* "converged" must never quietly mean "we stopped being able to check". */
|
|
110
|
+
unobserved?: string;
|
|
106
111
|
/** A write-guard rejected this attempt (deletion / shrink / probe-gaming). */
|
|
107
112
|
guardTripped?: boolean;
|
|
108
113
|
/** …and its edits were discarded. When a guard tripped and this is false, the
|
|
@@ -126,6 +131,7 @@ export interface FinalFixDeps {
|
|
|
126
131
|
ok: boolean;
|
|
127
132
|
reason: string;
|
|
128
133
|
failures?: string[];
|
|
134
|
+
unobserved?: string;
|
|
129
135
|
}>;
|
|
130
136
|
/** Labels of every currently-discoverable gate command (static + integration),
|
|
131
137
|
* for the shrink guard. Pure discovery — nothing is executed. */
|
|
@@ -38,12 +38,40 @@ export interface FinalGateOutcome {
|
|
|
38
38
|
* run never completes silently carrying an accepted defect. Empty/absent = none.
|
|
39
39
|
*/
|
|
40
40
|
openDebts?: AcceptDebt[];
|
|
41
|
+
/**
|
|
42
|
+
* Set (with the UNOBSERVED note) when the gate observed NOTHING dynamic — either
|
|
43
|
+
* no command was discoverable at all, or every discovered one was skipped as an
|
|
44
|
+
* environment gap. `ok` is still true (the statics did pass and there is nothing
|
|
45
|
+
* to fix), but this is NOT a PASS: the caller must record it as UNOBSERVED, never
|
|
46
|
+
* as "checked and fine". Absent ⇒ at least one dynamic command actually ran.
|
|
47
|
+
* See unobservedVerdict for the three-way verdict and the blocking decision.
|
|
48
|
+
*/
|
|
49
|
+
unobserved?: string;
|
|
41
50
|
}
|
|
42
51
|
/**
|
|
43
52
|
* The project's OWN whole-repo integration commands (test, then build — test
|
|
44
53
|
* first because it is the richer signal and the more common script). First
|
|
45
54
|
* manifest that exists wins, mirroring discoverHealthCommands. Empty means
|
|
46
55
|
* "nothing to run" — the static half may still gate.
|
|
56
|
+
*
|
|
57
|
+
* THE MANIFEST ALLOWLIST BELOW IS NARROW, AND THAT IS A KNOWN, MEASURED GAP: a
|
|
58
|
+
* C++/CMake project (no package.json) and a package.json whose only script is
|
|
59
|
+
* `verify` both discover NOTHING here. That outcome is now reported as UNOBSERVED
|
|
60
|
+
* rather than as a PASS (see unobservedVerdict), which makes the blindness loud and
|
|
61
|
+
* durable — it does not remove it. The gap itself stands.
|
|
62
|
+
*
|
|
63
|
+
* The obvious fix — harvest each task's own `## verified tooling` section, which
|
|
64
|
+
* DOES record the missing commands — was measured on 2026-07-27 and REFUTED. That
|
|
65
|
+
* section is model-authored and, despite its name, unverified: on godot-engine it
|
|
66
|
+
* yields 3 commands that exit 0 and 8 that exit non-zero, six of those for purely
|
|
67
|
+
* fabricated reasons (recorded without a required argument, pointing at files that
|
|
68
|
+
* do not exist, naming a test runner the project does not use) — so harvesting it
|
|
69
|
+
* turns that project's PASS into a FAIL citing "No scene path provided", plus ~15
|
|
70
|
+
* minutes of hang. Full numbers, the reproduction rig, and why no pre-execution
|
|
71
|
+
* filter can separate a fabricated command from a real one:
|
|
72
|
+
* scripts/harvest-verified-tooling-step0.ts. DO NOT re-propose the harvest without
|
|
73
|
+
* first fixing the PROVENANCE of `## verified tooling` (record cwd + exit code at
|
|
74
|
+
* authoring time); widening this allowlist tool-by-tool is not the fix either.
|
|
47
75
|
*/
|
|
48
76
|
export declare function discoverIntegrationCommands(cwd: string): {
|
|
49
77
|
ecosystem: string | null;
|
|
@@ -247,6 +275,48 @@ export declare function observabilityGapFailure(args: {
|
|
|
247
275
|
/** Is this runner spawnable (bare or via a known install location)? */
|
|
248
276
|
runnerResolvable: (bin: string) => boolean;
|
|
249
277
|
}): string | null;
|
|
278
|
+
/**
|
|
279
|
+
* The THIRD verdict. observabilityGapFailure above covers "commands were DISCOVERED
|
|
280
|
+
* but every one failed to spawn" — a rank-0 FAIL. It deliberately returns null for
|
|
281
|
+
* `attempted === 0`, and until now that silence fell straight through to
|
|
282
|
+
* `PASS — no integration command found (statics passed)`: the run-16 blindness class
|
|
283
|
+
* entering through a different door, where "we never checked" reads exactly like "we
|
|
284
|
+
* checked and it was fine". Measured 2026-07-27: IAR1 (C++/CMake, no package.json)
|
|
285
|
+
* shipped that verdict TWICE while carrying 2 and 3 open verify-FAIL debts, and
|
|
286
|
+
* godot-engine (package.json whose only script is `verify`) reproduces it live today.
|
|
287
|
+
*
|
|
288
|
+
* So: observed anything dynamic ⇒ PASS; discovered-but-all-spawn-failed ⇒ the
|
|
289
|
+
* existing FAIL; observed NOTHING ⇒ this note, carried on an `ok: true` outcome.
|
|
290
|
+
*
|
|
291
|
+
* WHY NON-BLOCKING (decided, not deferred — the evidence cuts both ways and this is
|
|
292
|
+
* the resolution):
|
|
293
|
+
* - Blocking's case: both real occurrences also carried open verify-FAIL debt, so
|
|
294
|
+
* the runs with no dynamic evidence were exactly the runs already known to be
|
|
295
|
+
* carrying defects.
|
|
296
|
+
* - Against, and decisive: (1) that debt is ALREADY surfaced unconditionally at the
|
|
297
|
+
* gate moment, on PASS as on FAIL — the IAR1 records literally read "PASS — no
|
|
298
|
+
* integration command found … UNRESOLVED VERIFY-FAIL DEBT still open (2)". The
|
|
299
|
+
* missing signal was never the debt, it was the word PASS endorsing the run, and
|
|
300
|
+
* that is what this fixes. (2) `ok: false` routes into the autofix picker, whose
|
|
301
|
+
* seed is `reason`; "no integration command is discoverable" is not fixable by
|
|
302
|
+
* editing code, so the highest-probability child response is to FABRICATE a
|
|
303
|
+
* runnable command to satisfy the gate — the same fabrication class that refuted
|
|
304
|
+
* the `## verified tooling` harvest (see discoverIntegrationCommands) and that had
|
|
305
|
+
* run 11's fix child `rm` a sibling's deliverable. (3) That harvest being refuted
|
|
306
|
+
* means IAR1 and godot-engine can NEVER discover a command, so blocking would end
|
|
307
|
+
* every non-npm run in `failed` permanently, with no remedy — the task's own I3
|
|
308
|
+
* ("show blocking does not block IAR1/godot post-Task-1") is unsatisfiable, and
|
|
309
|
+
* its stated consequence is to downgrade to a warning and say so. This is that.
|
|
310
|
+
* The teeth are elsewhere and are real: the verdict word changes, the gate trail says
|
|
311
|
+
* UNOBSERVED, and the caller records a durable final-gate debt that the NEXT run's
|
|
312
|
+
* gate re-surfaces (it can never auto-close — it is not static-class).
|
|
313
|
+
*/
|
|
314
|
+
export declare function unobservedVerdict(args: {
|
|
315
|
+
/** Dynamic commands the gate discovered and tried to run (0 ⇒ nothing existed). */
|
|
316
|
+
discovered: number;
|
|
317
|
+
/** Of those, how many actually RAN (a real pass or a real fail). */
|
|
318
|
+
observed: number;
|
|
319
|
+
}): string | null;
|
|
250
320
|
export { taskThatIntroduced };
|
|
251
321
|
/**
|
|
252
322
|
* Run the final gate: static analysis first, then the lockfile consistency
|
package/dist/task/final-gate.js
CHANGED
|
@@ -73,6 +73,25 @@ function makeHasTarget(cwd, target) {
|
|
|
73
73
|
* first because it is the richer signal and the more common script). First
|
|
74
74
|
* manifest that exists wins, mirroring discoverHealthCommands. Empty means
|
|
75
75
|
* "nothing to run" — the static half may still gate.
|
|
76
|
+
*
|
|
77
|
+
* THE MANIFEST ALLOWLIST BELOW IS NARROW, AND THAT IS A KNOWN, MEASURED GAP: a
|
|
78
|
+
* C++/CMake project (no package.json) and a package.json whose only script is
|
|
79
|
+
* `verify` both discover NOTHING here. That outcome is now reported as UNOBSERVED
|
|
80
|
+
* rather than as a PASS (see unobservedVerdict), which makes the blindness loud and
|
|
81
|
+
* durable — it does not remove it. The gap itself stands.
|
|
82
|
+
*
|
|
83
|
+
* The obvious fix — harvest each task's own `## verified tooling` section, which
|
|
84
|
+
* DOES record the missing commands — was measured on 2026-07-27 and REFUTED. That
|
|
85
|
+
* section is model-authored and, despite its name, unverified: on godot-engine it
|
|
86
|
+
* yields 3 commands that exit 0 and 8 that exit non-zero, six of those for purely
|
|
87
|
+
* fabricated reasons (recorded without a required argument, pointing at files that
|
|
88
|
+
* do not exist, naming a test runner the project does not use) — so harvesting it
|
|
89
|
+
* turns that project's PASS into a FAIL citing "No scene path provided", plus ~15
|
|
90
|
+
* minutes of hang. Full numbers, the reproduction rig, and why no pre-execution
|
|
91
|
+
* filter can separate a fabricated command from a real one:
|
|
92
|
+
* scripts/harvest-verified-tooling-step0.ts. DO NOT re-propose the harvest without
|
|
93
|
+
* first fixing the PROVENANCE of `## verified tooling` (record cwd + exit code at
|
|
94
|
+
* authoring time); widening this allowlist tool-by-tool is not the fix either.
|
|
76
95
|
*/
|
|
77
96
|
export function discoverIntegrationCommands(cwd) {
|
|
78
97
|
if (existsSync(path.join(cwd, 'package.json'))) {
|
|
@@ -771,6 +790,55 @@ export function observabilityGapFailure(args) {
|
|
|
771
790
|
+ `could even spawn in this environment${runnerNote}; `
|
|
772
791
|
+ `the gate observed nothing dynamic and cannot vouch for the assembled app`);
|
|
773
792
|
}
|
|
793
|
+
/**
|
|
794
|
+
* The THIRD verdict. observabilityGapFailure above covers "commands were DISCOVERED
|
|
795
|
+
* but every one failed to spawn" — a rank-0 FAIL. It deliberately returns null for
|
|
796
|
+
* `attempted === 0`, and until now that silence fell straight through to
|
|
797
|
+
* `PASS — no integration command found (statics passed)`: the run-16 blindness class
|
|
798
|
+
* entering through a different door, where "we never checked" reads exactly like "we
|
|
799
|
+
* checked and it was fine". Measured 2026-07-27: IAR1 (C++/CMake, no package.json)
|
|
800
|
+
* shipped that verdict TWICE while carrying 2 and 3 open verify-FAIL debts, and
|
|
801
|
+
* godot-engine (package.json whose only script is `verify`) reproduces it live today.
|
|
802
|
+
*
|
|
803
|
+
* So: observed anything dynamic ⇒ PASS; discovered-but-all-spawn-failed ⇒ the
|
|
804
|
+
* existing FAIL; observed NOTHING ⇒ this note, carried on an `ok: true` outcome.
|
|
805
|
+
*
|
|
806
|
+
* WHY NON-BLOCKING (decided, not deferred — the evidence cuts both ways and this is
|
|
807
|
+
* the resolution):
|
|
808
|
+
* - Blocking's case: both real occurrences also carried open verify-FAIL debt, so
|
|
809
|
+
* the runs with no dynamic evidence were exactly the runs already known to be
|
|
810
|
+
* carrying defects.
|
|
811
|
+
* - Against, and decisive: (1) that debt is ALREADY surfaced unconditionally at the
|
|
812
|
+
* gate moment, on PASS as on FAIL — the IAR1 records literally read "PASS — no
|
|
813
|
+
* integration command found … UNRESOLVED VERIFY-FAIL DEBT still open (2)". The
|
|
814
|
+
* missing signal was never the debt, it was the word PASS endorsing the run, and
|
|
815
|
+
* that is what this fixes. (2) `ok: false` routes into the autofix picker, whose
|
|
816
|
+
* seed is `reason`; "no integration command is discoverable" is not fixable by
|
|
817
|
+
* editing code, so the highest-probability child response is to FABRICATE a
|
|
818
|
+
* runnable command to satisfy the gate — the same fabrication class that refuted
|
|
819
|
+
* the `## verified tooling` harvest (see discoverIntegrationCommands) and that had
|
|
820
|
+
* run 11's fix child `rm` a sibling's deliverable. (3) That harvest being refuted
|
|
821
|
+
* means IAR1 and godot-engine can NEVER discover a command, so blocking would end
|
|
822
|
+
* every non-npm run in `failed` permanently, with no remedy — the task's own I3
|
|
823
|
+
* ("show blocking does not block IAR1/godot post-Task-1") is unsatisfiable, and
|
|
824
|
+
* its stated consequence is to downgrade to a warning and say so. This is that.
|
|
825
|
+
* The teeth are elsewhere and are real: the verdict word changes, the gate trail says
|
|
826
|
+
* UNOBSERVED, and the caller records a durable final-gate debt that the NEXT run's
|
|
827
|
+
* gate re-surfaces (it can never auto-close — it is not static-class).
|
|
828
|
+
*/
|
|
829
|
+
export function unobservedVerdict(args) {
|
|
830
|
+
if (args.observed > 0)
|
|
831
|
+
return null;
|
|
832
|
+
// Kept short ON PURPOSE: the run-level trail line slices the reason at 300 chars,
|
|
833
|
+
// and the whole point of this verdict is that the durable record carries it.
|
|
834
|
+
const why = args.discovered === 0 ?
|
|
835
|
+
'no integration, lockfile or boot command was discoverable here, so the gate ran '
|
|
836
|
+
+ 'nothing at all'
|
|
837
|
+
: `all ${args.discovered} discovered command(s) skipped as environment gaps, so the `
|
|
838
|
+
+ 'gate ran nothing observable';
|
|
839
|
+
return (`UNOBSERVED — NOT a pass: ${why}; statics passed, but this run produced NO evidence `
|
|
840
|
+
+ 'that the assembled product builds, boots or works.');
|
|
841
|
+
}
|
|
774
842
|
/**
|
|
775
843
|
* Boot check hit an address-in-use bind failure. If the port is held by one of OUR
|
|
776
844
|
* own orphaned gate children (a `dev`/`start` run), reap it and retry the boot once
|
|
@@ -867,8 +935,19 @@ export async function runFinalIntegrationGate(cwd, timeoutMs = 900_000, bootGrac
|
|
|
867
935
|
const lockCmds = discoverLockfileChecks(cwd);
|
|
868
936
|
const { cmds } = discoverIntegrationCommands(cwd);
|
|
869
937
|
const boot = discoverBootCommand(cwd);
|
|
938
|
+
// ZERO DISCOVERY IS UNOBSERVED, NEVER A PASS (see unobservedVerdict). Nothing was
|
|
939
|
+
// discovered, so nothing ran, so observabilityGapFailure (attempted === 0 → null) does
|
|
940
|
+
// not fire — and this outcome used to be reported as `PASS — no integration command
|
|
941
|
+
// found (statics passed)`, i.e. "we never checked" reading identically to "we checked
|
|
942
|
+
// and it was fine". IAR1 shipped that verdict TWICE while carrying open verify-FAIL
|
|
943
|
+
// debt (its .pi-tasks/TASK_AUTO_0001.md:31 and TASK_AUTO_0002.md:37). The outcome stays
|
|
944
|
+
// `ok: true` (non-blocking, justified at unobservedVerdict) but is now labelled, trailed
|
|
945
|
+
// and carried as debt by the caller. It needs no new command source, so unlike the
|
|
946
|
+
// harvest lever refuted at discoverIntegrationCommands it cannot inject a fabricated
|
|
947
|
+
// failure.
|
|
870
948
|
if (lockCmds.length === 0 && cmds.length === 0 && !boot && failures.length === 0) {
|
|
871
|
-
|
|
949
|
+
const note = unobservedVerdict({ discovered: 0, observed: 0 }) ?? '';
|
|
950
|
+
return withDebts({ ok: true, unobserved: note, reason: note });
|
|
872
951
|
}
|
|
873
952
|
const ran = [];
|
|
874
953
|
// Full-skip blindness counters (mx5 run 16): every dynamic spawn counts an
|
|
@@ -1046,10 +1125,20 @@ export async function runFinalIntegrationGate(cwd, timeoutMs = 900_000, bootGrac
|
|
|
1046
1125
|
});
|
|
1047
1126
|
}
|
|
1048
1127
|
const warningNote = warnings.length > 0 ? ` — WARNING: ${warnings.join('; WARNING: ')}` : '';
|
|
1128
|
+
// The same three-way verdict at the other zero-observation door: commands WERE
|
|
1129
|
+
// discovered, none spawn-failed (so the run-16 guard correctly stayed silent — every
|
|
1130
|
+
// skip was a tool-level env gap), and yet nothing ran. That was `statics passed
|
|
1131
|
+
// (integration commands not runnable here)`, which is the identical "we never checked"
|
|
1132
|
+
// silence wearing different words. Unchanged when anything at all was observed, so a
|
|
1133
|
+
// project with runnable commands is byte-for-byte unaffected.
|
|
1134
|
+
const unobserved = unobservedVerdict({ discovered: dynAttempted, observed: dynObserved });
|
|
1049
1135
|
return withDebts({
|
|
1050
1136
|
ok: true,
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1137
|
+
...(unobserved ? { unobserved } : {}),
|
|
1138
|
+
reason: (unobserved ? `${unobserved} — ` : '')
|
|
1139
|
+
+ (ran.length > 0 ?
|
|
1140
|
+
`statics + ${ran.map(c => `\`${c}\``).join(', ')} passed`
|
|
1141
|
+
: 'statics passed (integration commands not runnable here)')
|
|
1142
|
+
+ warningNote
|
|
1054
1143
|
});
|
|
1055
1144
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mjasnikovs/pi-task",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.4",
|
|
4
4
|
"description": "Deterministic task planning and spec-orchestration for local models — crash-safe /task pipelines with verify/enforce gates, a real-time remote web view, and web/docs/fetch/worker subagent tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|