@mjasnikovs/pi-task 0.18.21 → 0.18.23
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 +45 -4
- package/dist/task/accept-debt.js +76 -9
- package/dist/task/final-gate.d.ts +2 -9
- package/dist/task/final-gate.js +10 -22
- package/dist/task/frozen-conflict.d.ts +36 -0
- package/dist/task/frozen-conflict.js +206 -0
- package/dist/task/frozen-path-guard.d.ts +9 -0
- package/dist/task/frozen-path-guard.js +15 -0
- package/dist/task/gate-deps.d.ts +8 -0
- package/dist/task/gate-deps.js +39 -3
- package/dist/task/lint-fix.d.ts +15 -1
- package/dist/task/lint-fix.js +82 -1
- package/dist/task/phases.d.ts +1 -1
- package/dist/task/phases.js +31 -9
- package/dist/task/prohibition-probe.d.ts +7 -0
- package/dist/task/prohibition-probe.js +1 -1
- package/dist/task/task-gates.d.ts +19 -0
- package/dist/task/task-gates.js +71 -8
- package/dist/task/task-provenance.d.ts +45 -0
- package/dist/task/task-provenance.js +98 -0
- package/dist/task/verify-work.d.ts +17 -1
- package/dist/task/verify-work.js +55 -4
- package/dist/task/write-guard.d.ts +10 -0
- package/dist/task/write-guard.js +40 -0
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CrossTaskDeletion } from './task-provenance.js';
|
|
1
2
|
/**
|
|
2
3
|
* The verification child gets exactly two tools: `read` and `bash`.
|
|
3
4
|
*
|
|
@@ -27,6 +28,12 @@ export interface VerifyOutcome {
|
|
|
27
28
|
* AUTOFIX re-run, which cannot provision a missing tool. Only meaningful when
|
|
28
29
|
* ok === false. */
|
|
29
30
|
unobserved?: boolean;
|
|
31
|
+
/** The deterministic cross-task deletion findings (see task-provenance.ts) that
|
|
32
|
+
* were live when this verdict was produced: sibling tasks' committed
|
|
33
|
+
* deliverables this task's diff deletes. Carried on a FAIL so the gate loop
|
|
34
|
+
* can record each as a durable debt if the user ACCEPTs anyway — the deletion
|
|
35
|
+
* then ships in the next commit and the final gate must re-check it. */
|
|
36
|
+
crossTaskDeletions?: CrossTaskDeletion[];
|
|
30
37
|
}
|
|
31
38
|
/**
|
|
32
39
|
* Slice the delivered spec (GOAL / CONSTRAINTS / ACCEPTANCE / VERIFY) out of a
|
|
@@ -61,7 +68,7 @@ export declare function extractSpecForVerification(taskBody: string): string | n
|
|
|
61
68
|
* Guard: honest-clean fixture (prohibition in spec, probe silent) 5/5 PASS — no
|
|
62
69
|
* paranoia. Reverted-violation ≡ clean at the diff level (no entry → no finding).
|
|
63
70
|
*/
|
|
64
|
-
export declare function buildVerifyPrompt(spec: string, probeFindings?: string[], envNotes?: string, prohibitionFindings?: string[], skipEscapeFindings?: string[], contracts?: string, testAssemblyFindings?: string[], probeGamingFindings?: string[]): string;
|
|
71
|
+
export declare function buildVerifyPrompt(spec: string, probeFindings?: string[], envNotes?: string, prohibitionFindings?: string[], skipEscapeFindings?: string[], contracts?: string, testAssemblyFindings?: string[], probeGamingFindings?: string[], crossTaskDeletionFindings?: string[]): string;
|
|
65
72
|
/**
|
|
66
73
|
* Parse the child's verdict. Scans for the LAST `WORK-VERIFIED: PASS|FAIL|UNOBSERVED`
|
|
67
74
|
* marker (the model discusses before concluding, and bash output may echo the word
|
|
@@ -130,6 +137,15 @@ export interface VerificationDeps {
|
|
|
130
137
|
* underlying requirement is genuinely met rather than trusting the green check.
|
|
131
138
|
* Pure diff-text analysis; ABSENT or empty → no probe block. */
|
|
132
139
|
probeGamingProbe?: () => Promise<string[]>;
|
|
140
|
+
/**
|
|
141
|
+
* DETERMINISTIC cross-task deletion probe (see task-provenance.ts, mx5 run 12
|
|
142
|
+
* PROMPT 2): tracked files the task's diff DELETES whose introducing task (git
|
|
143
|
+
* provenance) differs from the current task — a sibling's committed deliverable
|
|
144
|
+
* destroyed, typically to green a check. Injected as prompt findings under rule
|
|
145
|
+
* 4d (MANDATORY + verdict-gating, the A/B-proven shape — buried rules score
|
|
146
|
+
* 0/5), and carried structurally on a FAIL outcome so an ACCEPT records each as
|
|
147
|
+
* a durable debt. ABSENT or empty → no block. */
|
|
148
|
+
crossTaskDeletionProbe?: () => Promise<CrossTaskDeletion[]>;
|
|
133
149
|
/**
|
|
134
150
|
* Result of the git-state guard for the MOST RECENT runChild call (see
|
|
135
151
|
* git-state-guard.ts): did the child mutate repo state (stash/checkout/file
|
package/dist/task/verify-work.js
CHANGED
|
@@ -71,6 +71,7 @@ import { USER_CANCELLED } from './child-runner.js';
|
|
|
71
71
|
import { buildEnvNotesBlock, ENV_NOTE_EMIT_INSTRUCTION, extractEnvNotes } from './env-notes.js';
|
|
72
72
|
import { buildContractsVerifyBlock } from './contracts.js';
|
|
73
73
|
import { findSkipEscapes, skipEscapeVerifyFindings } from './skip-escape.js';
|
|
74
|
+
import { crossTaskDeletionVerifyFindings } from './task-provenance.js';
|
|
74
75
|
/**
|
|
75
76
|
* The verification child gets exactly two tools: `read` and `bash`.
|
|
76
77
|
*
|
|
@@ -139,7 +140,7 @@ export function extractSpecForVerification(taskBody) {
|
|
|
139
140
|
* Guard: honest-clean fixture (prohibition in spec, probe silent) 5/5 PASS — no
|
|
140
141
|
* paranoia. Reverted-violation ≡ clean at the diff level (no entry → no finding).
|
|
141
142
|
*/
|
|
142
|
-
export function buildVerifyPrompt(spec, probeFindings, envNotes, prohibitionFindings, skipEscapeFindings, contracts, testAssemblyFindings, probeGamingFindings) {
|
|
143
|
+
export function buildVerifyPrompt(spec, probeFindings, envNotes, prohibitionFindings, skipEscapeFindings, contracts, testAssemblyFindings, probeGamingFindings, crossTaskDeletionFindings) {
|
|
143
144
|
const probeBlock = probeFindings && probeFindings.length > 0 ?
|
|
144
145
|
[
|
|
145
146
|
'SELF-VERIFICATION NOTICE (deterministic, computed by the orchestrator from the diff):',
|
|
@@ -166,6 +167,22 @@ export function buildVerifyPrompt(spec, probeFindings, envNotes, prohibitionFind
|
|
|
166
167
|
''
|
|
167
168
|
]
|
|
168
169
|
: [];
|
|
170
|
+
const crossTaskDeletionBlock = crossTaskDeletionFindings && crossTaskDeletionFindings.length > 0 ?
|
|
171
|
+
[
|
|
172
|
+
'CROSS-TASK DELETION NOTICE (deterministic, computed by the orchestrator from',
|
|
173
|
+
"git provenance over the task's diff): this task's work DELETED committed",
|
|
174
|
+
'deliverable(s) that a DIFFERENT, already-completed task introduced:',
|
|
175
|
+
...crossTaskDeletionFindings.map(f => `- ${f}`),
|
|
176
|
+
"A sibling task's verified, committed deliverable is not this task's to remove.",
|
|
177
|
+
'Deleting the file a checker complains about is the cheapest way to turn a red',
|
|
178
|
+
'check green — the finding vanishes with the file — and it destroys finished',
|
|
179
|
+
'work (rule 4d). A green check suite on the shrunken tree is NOT a waiver.',
|
|
180
|
+
"Unless THIS task's spec explicitly REQUIRES removing exactly that file, the",
|
|
181
|
+
'verdict is FAIL naming the deleted file and its owning task — even if every',
|
|
182
|
+
'check passes and the deletion made them pass.',
|
|
183
|
+
''
|
|
184
|
+
]
|
|
185
|
+
: [];
|
|
169
186
|
const probeGamingBlock = probeGamingFindings && probeGamingFindings.length > 0 ?
|
|
170
187
|
[
|
|
171
188
|
'CHECK-GAMING NOTICE (deterministic, computed by the orchestrator from the',
|
|
@@ -235,6 +252,7 @@ export function buildVerifyPrompt(spec, probeFindings, envNotes, prohibitionFind
|
|
|
235
252
|
...contractsBlock,
|
|
236
253
|
...probeBlock,
|
|
237
254
|
...prohibitionBlock,
|
|
255
|
+
...crossTaskDeletionBlock,
|
|
238
256
|
...probeGamingBlock,
|
|
239
257
|
...skipEscapeBlock,
|
|
240
258
|
...testAssemblyBlock,
|
|
@@ -359,6 +377,17 @@ export function buildVerifyPrompt(spec, probeFindings, envNotes, prohibitionFind
|
|
|
359
377
|
' check, not implementing the behavior). If the requirement is genuinely unmet while',
|
|
360
378
|
' the check passes, the verdict is FAIL naming the gamed check and the real gap.',
|
|
361
379
|
'',
|
|
380
|
+
"4d. A SIBLING TASK'S COMMITTED DELIVERABLE IS NOT THIS TASK'S TO DELETE — check the",
|
|
381
|
+
" task's own diff (git) for DELETED tracked files. A file that a DIFFERENT completed",
|
|
382
|
+
" task introduced and committed, deleted by this task's work, is finished work",
|
|
383
|
+
' destroyed — usually to make a red check go green (the file the checker complains',
|
|
384
|
+
' about simply disappears; that is quieting the messenger, rule 4c, not fixing the',
|
|
385
|
+
' defect). A passing check suite on the shrunken tree is not a waiver. Only two',
|
|
386
|
+
" outcomes are not a FAIL: THIS task's spec explicitly requires that removal, or",
|
|
387
|
+
' the deletion is a genuine relocation (the same file lives on elsewhere in the',
|
|
388
|
+
' tree). Otherwise the verdict is FAIL naming the deleted file and the task that',
|
|
389
|
+
' owns it.',
|
|
390
|
+
'',
|
|
362
391
|
'5. The ONLY thing you may assume is already provided is a genuinely EXTERNAL running',
|
|
363
392
|
' service or network resource (a database server, an API host) that the project',
|
|
364
393
|
' documents as a prerequisite. Before you rely on that assumption, PROBE for the',
|
|
@@ -531,6 +560,18 @@ export async function runWorkVerification(deps) {
|
|
|
531
560
|
probeGaming = [];
|
|
532
561
|
}
|
|
533
562
|
}
|
|
563
|
+
// Cross-task deletion findings feed the prompt (rule 4d) AND ride on a FAIL
|
|
564
|
+
// outcome (structured) so an ACCEPT can record them as durable debts. A probe
|
|
565
|
+
// failure must never block verification.
|
|
566
|
+
let crossDeletions = [];
|
|
567
|
+
if (deps.crossTaskDeletionProbe) {
|
|
568
|
+
try {
|
|
569
|
+
crossDeletions = await deps.crossTaskDeletionProbe();
|
|
570
|
+
}
|
|
571
|
+
catch {
|
|
572
|
+
crossDeletions = [];
|
|
573
|
+
}
|
|
574
|
+
}
|
|
534
575
|
// Environment facts from earlier gate children (best-effort; a cache failure
|
|
535
576
|
// must never block verification).
|
|
536
577
|
let envNotes = '';
|
|
@@ -566,7 +607,7 @@ export async function runWorkVerification(deps) {
|
|
|
566
607
|
for (let attempt = 1;; attempt++) {
|
|
567
608
|
let text;
|
|
568
609
|
try {
|
|
569
|
-
text = await deps.runChild(VERIFY_TOOLS, buildVerifyPrompt(deps.spec, findings, envNotes, prohibitions, skipEscapes, contracts, testAssembly, probeGaming), deps.signal);
|
|
610
|
+
text = await deps.runChild(VERIFY_TOOLS, buildVerifyPrompt(deps.spec, findings, envNotes, prohibitions, skipEscapes, contracts, testAssembly, probeGaming, crossTaskDeletionVerifyFindings(crossDeletions)), deps.signal);
|
|
570
611
|
}
|
|
571
612
|
catch (err) {
|
|
572
613
|
if (err instanceof Error && err.message === USER_CANCELLED)
|
|
@@ -603,15 +644,25 @@ export async function runWorkVerification(deps) {
|
|
|
603
644
|
return { ok: true };
|
|
604
645
|
if (verdict.detail === 'no verdict emitted' && attempt === 1)
|
|
605
646
|
continue;
|
|
647
|
+
// Structured cross-task deletion findings ride on every FAIL outcome: if the
|
|
648
|
+
// human ACCEPTs the failing artifact, the deletions ship in the next commit
|
|
649
|
+
// and the gate loop must record each as a durable debt (see task-gates.ts).
|
|
650
|
+
const deletions = crossDeletions.length > 0 ? { crossTaskDeletions: crossDeletions } : {};
|
|
606
651
|
// UNOBSERVED (rule 5c): a spec-required behavioral check could not run because
|
|
607
652
|
// its tooling is absent. Block like any FAIL, but flag it so the gate hands it
|
|
608
653
|
// straight to the human — re-running the impl turn cannot install a missing tool.
|
|
609
654
|
if (verdict.unobserved) {
|
|
610
|
-
return {
|
|
655
|
+
return {
|
|
656
|
+
ok: false,
|
|
657
|
+
unobserved: true,
|
|
658
|
+
reason: `work unobserved: ${verdict.detail}`,
|
|
659
|
+
...deletions
|
|
660
|
+
};
|
|
611
661
|
}
|
|
612
662
|
return {
|
|
613
663
|
ok: false,
|
|
614
|
-
reason: `work did not verify: ${verdict.detail}${verdict.detail === 'no verdict emitted' ? ' (after verify retry)' : ''}
|
|
664
|
+
reason: `work did not verify: ${verdict.detail}${verdict.detail === 'no verdict emitted' ? ' (after verify retry)' : ''}`,
|
|
665
|
+
...deletions
|
|
615
666
|
};
|
|
616
667
|
}
|
|
617
668
|
}
|
|
@@ -36,6 +36,16 @@ export interface TreeChangeSummary {
|
|
|
36
36
|
* without a repo.
|
|
37
37
|
*/
|
|
38
38
|
export declare function parseTreeChanges(porcelain: string): TreeChangeSummary;
|
|
39
|
+
/**
|
|
40
|
+
* Parse `git diff --name-status` output (tab-separated: `M\tpath`, `A\tpath`,
|
|
41
|
+
* `D\tpath`, `R100\told\tnew`, `C75\told\tnew`) into the same change summary. Used
|
|
42
|
+
* where the working tree is already clean and the last COMMIT's diff is the record
|
|
43
|
+
* of the task's changes (the post-enforce re-verify fallback). A rename entry
|
|
44
|
+
* contributes its source to `deleted` and its target to `added`, matching
|
|
45
|
+
* parseTreeChanges so the relocation allowance below reads both shapes identically;
|
|
46
|
+
* a copy's source still exists, so only its target counts (as added).
|
|
47
|
+
*/
|
|
48
|
+
export declare function parseNameStatusChanges(nameStatus: string): TreeChangeSummary;
|
|
39
49
|
/**
|
|
40
50
|
* The deletions a fix pass may NOT make: every deleted tracked file whose name does
|
|
41
51
|
* not reappear among the added files (a relocation keeps the file, under the same
|
package/dist/task/write-guard.js
CHANGED
|
@@ -75,6 +75,46 @@ export function parseTreeChanges(porcelain) {
|
|
|
75
75
|
}
|
|
76
76
|
return { modified: [...modified], deleted: [...deleted], added: [...added] };
|
|
77
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Parse `git diff --name-status` output (tab-separated: `M\tpath`, `A\tpath`,
|
|
80
|
+
* `D\tpath`, `R100\told\tnew`, `C75\told\tnew`) into the same change summary. Used
|
|
81
|
+
* where the working tree is already clean and the last COMMIT's diff is the record
|
|
82
|
+
* of the task's changes (the post-enforce re-verify fallback). A rename entry
|
|
83
|
+
* contributes its source to `deleted` and its target to `added`, matching
|
|
84
|
+
* parseTreeChanges so the relocation allowance below reads both shapes identically;
|
|
85
|
+
* a copy's source still exists, so only its target counts (as added).
|
|
86
|
+
*/
|
|
87
|
+
export function parseNameStatusChanges(nameStatus) {
|
|
88
|
+
const modified = new Set();
|
|
89
|
+
const deleted = new Set();
|
|
90
|
+
const added = new Set();
|
|
91
|
+
for (const raw of nameStatus.split('\n')) {
|
|
92
|
+
const parts = raw.split('\t').map(s => s.trim());
|
|
93
|
+
const code = parts[0] ?? '';
|
|
94
|
+
if (code.length === 0 || !parts[1])
|
|
95
|
+
continue;
|
|
96
|
+
if (code.startsWith('R')) {
|
|
97
|
+
deleted.add(parts[1]);
|
|
98
|
+
if (parts[2])
|
|
99
|
+
added.add(parts[2]);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (code.startsWith('C')) {
|
|
103
|
+
added.add(parts[2] ?? parts[1]);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (code.startsWith('D')) {
|
|
107
|
+
deleted.add(parts[1]);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (code.startsWith('A')) {
|
|
111
|
+
added.add(parts[1]);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
modified.add(parts[1]);
|
|
115
|
+
}
|
|
116
|
+
return { modified: [...modified], deleted: [...deleted], added: [...added] };
|
|
117
|
+
}
|
|
78
118
|
const basename = (p) => {
|
|
79
119
|
const i = p.lastIndexOf('/');
|
|
80
120
|
return i === -1 ? p : p.slice(i + 1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mjasnikovs/pi-task",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.23",
|
|
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",
|