@mjasnikovs/pi-task 0.18.1 → 0.18.2
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.
|
@@ -45,6 +45,32 @@ function coverageRepromptHint(missing) {
|
|
|
45
45
|
+ 'task your previous list already had (reworded freely) PLUS tasks covering '
|
|
46
46
|
+ 'the areas above. Output every task, one "- [ ] " line each, nothing else.]');
|
|
47
47
|
}
|
|
48
|
+
// Deterministic distrust floor for the coverage gate. The gate's judge is the
|
|
49
|
+
// same stochastic model as the decompose call it guards, and live (mx5 2026-07-08,
|
|
50
|
+
// A/B N=10) it rubber-stamps a 1-task plan for an 18KB spec 3/10 times — always
|
|
51
|
+
// as the bare "COVERAGE: COMPLETE" line, which is byte-identical to a legitimate
|
|
52
|
+
// verdict, so the rubber-stamp is NOT detectable from the judge's output. The
|
|
53
|
+
// distrust signal must come from the input: a plan this small for a spec this
|
|
54
|
+
// large is near-certainly the known degenerate-decompose flake (healthy runs on
|
|
55
|
+
// the same inputs produce 10–30 titles). The floor only ever forces a REGENERATION
|
|
56
|
+
// — it never rejects a plan on count alone (the v0.13.34 objection), so a model
|
|
57
|
+
// that insists twice still ships its small plan, with a warning.
|
|
58
|
+
const SUSPECT_PLAN_MAX_TITLES = 2;
|
|
59
|
+
const SUSPECT_PLAN_MIN_SPEC_CHARS = 4000;
|
|
60
|
+
function isSuspectPlan(titles, featureForModel) {
|
|
61
|
+
return (titles.length > 0
|
|
62
|
+
&& titles.length <= SUSPECT_PLAN_MAX_TITLES
|
|
63
|
+
&& featureForModel.length >= SUSPECT_PLAN_MIN_SPEC_CHARS);
|
|
64
|
+
}
|
|
65
|
+
/** Reprompt prefix for a suspect (degenerate-count) list; unlike
|
|
66
|
+
* coverageRepromptHint there is no judge verdict yet, so no missing areas. */
|
|
67
|
+
function suspectPlanHint(count) {
|
|
68
|
+
return (`[SYSTEM NOTE: Your previous answer contained only ${count} task(s), which `
|
|
69
|
+
+ 'cannot decompose a feature specification of this size — it was almost '
|
|
70
|
+
+ 'certainly an incomplete generation. Regenerate the FULL ordered checkbox '
|
|
71
|
+
+ 'list for the ENTIRE feature, covering every part of the spec end to end. '
|
|
72
|
+
+ 'Output every task, one "- [ ] " line each, nothing else.]');
|
|
73
|
+
}
|
|
48
74
|
// Matches pi's @-file completion token (a path after @, until whitespace).
|
|
49
75
|
const MENTION_RE = /(?:^|\s)@([^\s]+)/g;
|
|
50
76
|
// Trailing punctuation a user naturally types AFTER an @-mention when it sits in
|
|
@@ -411,6 +437,20 @@ export async function planAuto(ctx, cwd, feature, deps) {
|
|
|
411
437
|
const listRaw = await deps.runChild('auto-decompose', 'read', decomposePrompt);
|
|
412
438
|
let planTitles = parseDecomposeList(listRaw);
|
|
413
439
|
logPlanDebug(cwd, `decompose produced ${planTitles.length} title(s)`);
|
|
440
|
+
// Distrust floor (see isSuspectPlan): a ≤2-title plan for a multi-KB spec is
|
|
441
|
+
// regenerated once BEFORE the judge runs — the judge cannot be trusted to
|
|
442
|
+
// catch it (3/10 live false-pass) and a hinted retry heals it reliably
|
|
443
|
+
// (5/5 live). Longer list wins; a still-suspect plan falls through to the
|
|
444
|
+
// judge loop as before, so this never blocks planning.
|
|
445
|
+
if (isSuspectPlan(planTitles, featureForModel)) {
|
|
446
|
+
logPlanDebug(cwd, `decompose suspect (${planTitles.length} title(s) for a ${featureForModel.length}-char spec)`
|
|
447
|
+
+ ` — raw output: ${listRaw.trim().slice(0, 300)}`);
|
|
448
|
+
const retryRaw = await deps.runChild('auto-decompose', 'read', prependHint(suspectPlanHint(planTitles.length), decomposePrompt));
|
|
449
|
+
const retryTitles = parseDecomposeList(retryRaw);
|
|
450
|
+
logPlanDebug(cwd, `decompose suspect-retry produced ${retryTitles.length} title(s)`);
|
|
451
|
+
if (retryTitles.length > planTitles.length)
|
|
452
|
+
planTitles = retryTitles;
|
|
453
|
+
}
|
|
414
454
|
// Coverage gate: a stochastic degenerate completion (live mx5: ONE task +
|
|
415
455
|
// natural EOS for an 18KB design doc) is nonempty, so the length guard below
|
|
416
456
|
// never fires and the whole run "completes" after one task. Judge the list
|
|
@@ -442,6 +482,14 @@ export async function planAuto(ctx, cwd, feature, deps) {
|
|
|
442
482
|
unresolvedMissing = null;
|
|
443
483
|
logPlanDebug(cwd, `decompose-coverage round ${round + 1}: `
|
|
444
484
|
+ (verdict === null ? 'no verdict — accepting list' : 'COMPLETE'));
|
|
485
|
+
// A COMPLETE on a still-suspect plan is the judge's known live
|
|
486
|
+
// false-pass mode (bare verdict, indistinguishable from a real one).
|
|
487
|
+
// The plan still ships — the floor never rejects on count — but
|
|
488
|
+
// never silently: the user decides whether to trust it.
|
|
489
|
+
if (isSuspectPlan(planTitles, featureForModel)) {
|
|
490
|
+
ctx.ui.notify(`/task-auto: only ${planTitles.length} task(s) planned for a large spec`
|
|
491
|
+
+ ' and the regeneration did not grow the list — review the plan before running.', 'warning');
|
|
492
|
+
}
|
|
445
493
|
break;
|
|
446
494
|
}
|
|
447
495
|
unresolvedMissing = verdict.missing;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mjasnikovs/pi-task",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.2",
|
|
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",
|