@mjasnikovs/pi-task 0.42.3 → 0.42.5
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 +3 -2
- package/dist/task/accept-debt.js +50 -14
- package/dist/task/auto-orchestrator.js +8 -6
- package/dist/task/command-run.d.ts +12 -1
- package/dist/task/command-run.js +13 -0
- package/dist/task/deferred-breakage.d.ts +19 -0
- package/dist/task/deferred-breakage.js +98 -22
- package/dist/task/gate-deps.d.ts +11 -0
- package/dist/task/gate-deps.js +49 -14
- package/dist/task/gate-resolution.js +1 -0
- package/dist/task/health-baseline.d.ts +21 -4
- package/dist/task/health-baseline.js +41 -9
- package/dist/task/health-repair.d.ts +19 -4
- package/dist/task/health-repair.js +18 -6
- package/dist/task/phases.js +75 -75
- package/dist/task/repo-health-check.d.ts +21 -9
- package/dist/task/repo-health-check.js +49 -12
- package/dist/task/run-context.d.ts +14 -0
- package/dist/task/run-context.js +29 -2
- package/dist/task/verify-work.d.ts +7 -1
- package/dist/task/verify-work.js +55 -13
- package/dist/task/yolo.d.ts +8 -4
- package/dist/task/yolo.js +17 -9
- package/package.json +1 -1
package/dist/task/verify-work.js
CHANGED
|
@@ -42,7 +42,8 @@ import { buildEnvNotesBlock, ENV_NOTE_EMIT_INSTRUCTION, extractEnvNotes } from '
|
|
|
42
42
|
import { buildContractsVerifyBlock } from './contracts.js';
|
|
43
43
|
import { findSkipEscapes, skipEscapeVerifyFindings } from './skip-escape.js';
|
|
44
44
|
import { crossTaskDeletionVerifyFindings } from './task-provenance.js';
|
|
45
|
-
import { classifyHealthDelta, inheritedHealthFindings } from './health-baseline.js';
|
|
45
|
+
import { classifyHealthDelta, inheritedHealthFindings, regressedCommands } from './health-baseline.js';
|
|
46
|
+
import { describeHealthFailures } from './repo-health-check.js';
|
|
46
47
|
import { parseSpec, sliceSpecSection } from './spec-model.js';
|
|
47
48
|
import { qaKindsFromRecord } from './qa-transcript.js';
|
|
48
49
|
import { annotateConstraints, anyBinding, renderConstraintPolicy } from './constraint-policy.js';
|
|
@@ -72,6 +73,7 @@ const VERIFY_TOOLS = 'read,bash';
|
|
|
72
73
|
export const VERIFY_FAIL_PREFIX = {
|
|
73
74
|
'repo-health': 'repo health:',
|
|
74
75
|
'static-checks': 'static checks:',
|
|
76
|
+
'test-suite': 'test suite:',
|
|
75
77
|
unobserved: 'work unobserved:',
|
|
76
78
|
'model-verdict': 'work did not verify:',
|
|
77
79
|
'harness-fault': 'verification pass could not run:'
|
|
@@ -103,6 +105,14 @@ export function failClassOfReason(reason) {
|
|
|
103
105
|
export function isStaticClass(cls) {
|
|
104
106
|
return cls === 'repo-health' || cls === 'static-checks';
|
|
105
107
|
}
|
|
108
|
+
/** The class a red health result is minted under: the suite's when any test is red. */
|
|
109
|
+
function healthFailClass(failing) {
|
|
110
|
+
return failing.some(c => c.kind === 'test') ? 'test-suite' : 'repo-health';
|
|
111
|
+
}
|
|
112
|
+
/** Does this class name the deterministic whole-repo check, suite included? */
|
|
113
|
+
export function isHealthClass(cls) {
|
|
114
|
+
return isStaticClass(cls) || cls === 'test-suite';
|
|
115
|
+
}
|
|
106
116
|
/**
|
|
107
117
|
* The delivered spec's TEXT, for the children that must read its prose verbatim.
|
|
108
118
|
* The slicing itself lives in spec-model.ts beside the parser, so "the spec
|
|
@@ -182,8 +192,8 @@ const PROBE_ADAPTERS = [
|
|
|
182
192
|
]
|
|
183
193
|
}),
|
|
184
194
|
/**
|
|
185
|
-
* PRE-EXISTING repo health (see health-baseline.ts):
|
|
186
|
-
*
|
|
195
|
+
* PRE-EXISTING repo health (see health-baseline.ts): checks that were ALREADY
|
|
196
|
+
* failing, with the same exit code, before this task started. They used to be an
|
|
187
197
|
* absolute FAIL that short-circuited the whole pass, so the task answered for
|
|
188
198
|
* a sibling's defect and its own probe findings were never computed. As a row
|
|
189
199
|
* they are stated to the child instead: judge this task's work, and do not
|
|
@@ -197,7 +207,7 @@ const PROBE_ADAPTERS = [
|
|
|
197
207
|
ruleId: '4h',
|
|
198
208
|
block: findings => [
|
|
199
209
|
'INHERITED REPO-HEALTH NOTICE (deterministic, computed by the orchestrator by',
|
|
200
|
-
"re-running the project's own
|
|
210
|
+
"re-running the project's own checks and comparing their exit codes against the",
|
|
201
211
|
'baseline taken before this task started): these checks were ALREADY failing,',
|
|
202
212
|
'with the same exit code, before any of this work existed:',
|
|
203
213
|
...findings.map(f => `- ${f}`),
|
|
@@ -208,13 +218,16 @@ const PROBE_ADAPTERS = [
|
|
|
208
218
|
],
|
|
209
219
|
rule: [
|
|
210
220
|
"4h. AN INHERITED RED CHECK IS NOT THIS TASK'S FAIL, AND NOT ITS PROOF — when the",
|
|
211
|
-
' INHERITED REPO-HEALTH NOTICE above names a check, that check
|
|
221
|
+
' INHERITED REPO-HEALTH NOTICE above names a check, that check exited the same way',
|
|
212
222
|
' before this task ran. Do NOT fail this work for it: the defect belongs to',
|
|
213
223
|
' whatever put it there, it is recorded as durable debt, and the run-end gate',
|
|
214
224
|
' re-checks it. Do NOT lean on it either — a command that was already exiting',
|
|
215
225
|
" non-zero tells you nothing about this task's behavior, so verify that behavior",
|
|
216
226
|
' another way. A check that is failing DIFFERENTLY, or one absent from the notice,',
|
|
217
|
-
" is this task's to answer for in the ordinary way."
|
|
227
|
+
" is this task's to answer for in the ordinary way. A TEST command is the",
|
|
228
|
+
' exception to the exit code: it exits the same way for one failing test or',
|
|
229
|
+
" fifty. Run it, and a test that fails because of THIS work is this task's FAIL,",
|
|
230
|
+
' whatever the spec says about it.'
|
|
218
231
|
]
|
|
219
232
|
}),
|
|
220
233
|
/**
|
|
@@ -905,18 +918,47 @@ export async function runWorkVerification(deps) {
|
|
|
905
918
|
if (deps.repoHealth) {
|
|
906
919
|
stage('repo health');
|
|
907
920
|
const h = await deps.repoHealth();
|
|
908
|
-
|
|
921
|
+
// `ok` is not the whole verdict: a suite this tree no longer finds observes
|
|
922
|
+
// nothing, so nothing fails, and only the differential sees it went away.
|
|
923
|
+
// Establishing a baseline can cost a worktree health run, so it is asked for
|
|
924
|
+
// only in the two shapes it can speak to.
|
|
925
|
+
const suiteGone = (h.commands ?? []).some(c => c.gap === 'empty-suite');
|
|
926
|
+
if (!h.ok || suiteGone) {
|
|
909
927
|
const baseline = deps.healthBaseline ? await deps.healthBaseline() : null;
|
|
910
|
-
|
|
928
|
+
const before = baseline?.outcome ?? null;
|
|
929
|
+
if (classifyHealthDelta(before, h) === 'regressed') {
|
|
930
|
+
// Named after what REGRESSED, not after whatever failed first: a lint
|
|
931
|
+
// red on arrival would otherwise stand in for the suite this task broke.
|
|
932
|
+
const regressed = regressedCommands(before, h);
|
|
933
|
+
if (regressed.length === 0) {
|
|
934
|
+
return {
|
|
935
|
+
ok: false,
|
|
936
|
+
failClass: 'repo-health',
|
|
937
|
+
reason: `repo health: ${h.reason}`,
|
|
938
|
+
health: h
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
const failClass = healthFailClass(regressed);
|
|
911
942
|
return {
|
|
912
943
|
ok: false,
|
|
913
|
-
failClass
|
|
914
|
-
reason:
|
|
915
|
-
health:
|
|
944
|
+
failClass,
|
|
945
|
+
reason: `${VERIFY_FAIL_PREFIX[failClass]} ${describeHealthFailures(regressed)}`,
|
|
946
|
+
health: {
|
|
947
|
+
...h,
|
|
948
|
+
ok: false,
|
|
949
|
+
commands: regressed,
|
|
950
|
+
output: regressed[0].output ?? h.output
|
|
951
|
+
}
|
|
916
952
|
};
|
|
917
953
|
}
|
|
918
|
-
|
|
919
|
-
|
|
954
|
+
if (!h.ok) {
|
|
955
|
+
pre.repoHealth = inheritedHealthFindings(h);
|
|
956
|
+
const failing = h.commands?.filter(c => c.outcome === 'fail') ?? [];
|
|
957
|
+
inheritedHealth =
|
|
958
|
+
failing.length > 0 ?
|
|
959
|
+
`${VERIFY_FAIL_PREFIX[healthFailClass(failing)]} ${describeHealthFailures(failing)} — already failing before this task`
|
|
960
|
+
: `repo health: ${h.reason} — already failing before this task`;
|
|
961
|
+
}
|
|
920
962
|
}
|
|
921
963
|
}
|
|
922
964
|
const inherited = inheritedHealth === undefined ? {} : { inheritedHealth };
|
package/dist/task/yolo.d.ts
CHANGED
|
@@ -36,6 +36,10 @@ export type YoloPick = {
|
|
|
36
36
|
* demotion — an answer proven to name an unverified API identifier. Auto-accepting
|
|
37
37
|
* that would re-promote exactly the invention the demotion exists to stop, so a
|
|
38
38
|
* machine may never take it; a human still can.
|
|
39
|
+
*
|
|
40
|
+
* An option that defers a breakage to an owner nobody is, is passed over here
|
|
41
|
+
* rather than at each caller: the clarify generator, the plan review and an
|
|
42
|
+
* UNKNOWN's own suggestion reach a machine with no guard in front of them.
|
|
39
43
|
*/
|
|
40
44
|
export declare function yoloPickAnswer(enabled: boolean, opts: {
|
|
41
45
|
suggested?: string;
|
|
@@ -45,11 +49,11 @@ export declare function yoloPickAnswer(enabled: boolean, opts: {
|
|
|
45
49
|
/**
|
|
46
50
|
* The same policy expressed over an {@link AutoAnswer}, for the grill site. Of the
|
|
47
51
|
* five `reason` tags an unknown can carry — `api-synthesis`, `deferred-breakage`,
|
|
48
|
-
* `integration`, `threw`, `model-unknown` —
|
|
49
|
-
*
|
|
52
|
+
* `integration`, `threw`, `model-unknown` — `api-synthesis` is unsafe by its tag:
|
|
53
|
+
* the answer names an API nobody verified. A `deferred-breakage` suggestion is the
|
|
54
|
+
* deferral itself, which `yoloPickAnswer` passes over for every site. The other
|
|
50
55
|
* three carry an ordinary best-effort recommendation, which is precisely what a
|
|
51
|
-
* human would be shown as the green card.
|
|
52
|
-
* never by pattern-matching the answer text.
|
|
56
|
+
* human would be shown as the green card.
|
|
53
57
|
*/
|
|
54
58
|
export declare function yoloPickAutoAnswer(enabled: boolean, auto: AutoAnswer): YoloPick;
|
|
55
59
|
/**
|
package/dist/task/yolo.js
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
* the config.
|
|
30
30
|
*/
|
|
31
31
|
import { getConfig } from '../config/config.js';
|
|
32
|
+
import { defersBreakage } from './deferred-breakage.js';
|
|
32
33
|
/**
|
|
33
34
|
* Visible provenance marker on the artifacts an auto-pick writes: gate trail lines
|
|
34
35
|
* and the task file's Q&A record (qa-transcript.ts `QA_PROVENANCE`). A later audit
|
|
@@ -53,6 +54,10 @@ export function isYoloMode() {
|
|
|
53
54
|
* demotion — an answer proven to name an unverified API identifier. Auto-accepting
|
|
54
55
|
* that would re-promote exactly the invention the demotion exists to stop, so a
|
|
55
56
|
* machine may never take it; a human still can.
|
|
57
|
+
*
|
|
58
|
+
* An option that defers a breakage to an owner nobody is, is passed over here
|
|
59
|
+
* rather than at each caller: the clarify generator, the plan review and an
|
|
60
|
+
* UNKNOWN's own suggestion reach a machine with no guard in front of them.
|
|
56
61
|
*/
|
|
57
62
|
export function yoloPickAnswer(enabled, opts) {
|
|
58
63
|
if (!enabled)
|
|
@@ -60,20 +65,26 @@ export function yoloPickAnswer(enabled, opts) {
|
|
|
60
65
|
if (opts.unsafe !== undefined && opts.unsafe.length > 0) {
|
|
61
66
|
return { kind: 'skip', note: opts.unsafe };
|
|
62
67
|
}
|
|
63
|
-
const
|
|
64
|
-
if (
|
|
68
|
+
const offered = [opts.suggested, opts.alt].filter((o) => o !== undefined && o.trim().length > 0);
|
|
69
|
+
if (offered.length === 0)
|
|
65
70
|
return { kind: 'skip', note: 'no recommended option to take' };
|
|
71
|
+
const pick = offered.find(o => !defersBreakage(o));
|
|
72
|
+
if (pick === undefined) {
|
|
73
|
+
return {
|
|
74
|
+
kind: 'skip',
|
|
75
|
+
note: 'the recommended answer leaves a test or build failing for an owner that does not exist — needs a human'
|
|
76
|
+
};
|
|
66
77
|
}
|
|
67
78
|
return { kind: 'answer', answer: pick };
|
|
68
79
|
}
|
|
69
80
|
/**
|
|
70
81
|
* The same policy expressed over an {@link AutoAnswer}, for the grill site. Of the
|
|
71
82
|
* five `reason` tags an unknown can carry — `api-synthesis`, `deferred-breakage`,
|
|
72
|
-
* `integration`, `threw`, `model-unknown` —
|
|
73
|
-
*
|
|
83
|
+
* `integration`, `threw`, `model-unknown` — `api-synthesis` is unsafe by its tag:
|
|
84
|
+
* the answer names an API nobody verified. A `deferred-breakage` suggestion is the
|
|
85
|
+
* deferral itself, which `yoloPickAnswer` passes over for every site. The other
|
|
74
86
|
* three carry an ordinary best-effort recommendation, which is precisely what a
|
|
75
|
-
* human would be shown as the green card.
|
|
76
|
-
* never by pattern-matching the answer text.
|
|
87
|
+
* human would be shown as the green card.
|
|
77
88
|
*/
|
|
78
89
|
export function yoloPickAutoAnswer(enabled, auto) {
|
|
79
90
|
if (!enabled)
|
|
@@ -85,9 +96,6 @@ export function yoloPickAutoAnswer(enabled, auto) {
|
|
|
85
96
|
...(auto.alt !== undefined && { alt: auto.alt }),
|
|
86
97
|
...(auto.reason === 'api-synthesis' && {
|
|
87
98
|
unsafe: 'the suggested answer names an unverified API identifier — needs a human'
|
|
88
|
-
}),
|
|
89
|
-
...(auto.reason === 'deferred-breakage' && {
|
|
90
|
-
unsafe: 'the suggested answer leaves a test or build failing for an owner that does not exist — needs a human'
|
|
91
99
|
})
|
|
92
100
|
});
|
|
93
101
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mjasnikovs/pi-task",
|
|
3
|
-
"version": "0.42.
|
|
3
|
+
"version": "0.42.5",
|
|
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",
|