taskplane 0.22.2 → 0.22.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.
|
@@ -154,6 +154,8 @@ interface TaskState {
|
|
|
154
154
|
persistentReviewerKill: (() => void) | null;
|
|
155
155
|
/** TP-057: Signal counter for the persistent reviewer (monotonically increasing). */
|
|
156
156
|
persistentReviewerSignalNum: number;
|
|
157
|
+
/** Reviewer respawn counter — circuit breaker to prevent infinite respawn loops. */
|
|
158
|
+
reviewerRespawnCount: number;
|
|
157
159
|
totalIterations: number;
|
|
158
160
|
stepStatuses: Map<number, StepInfo>;
|
|
159
161
|
}
|
|
@@ -172,7 +174,7 @@ function freshState(): TaskState {
|
|
|
172
174
|
reviewerInputTokens: 0, reviewerOutputTokens: 0, reviewerCacheReadTokens: 0, reviewerCacheWriteTokens: 0,
|
|
173
175
|
reviewerCostUsd: 0, reviewerContextPct: 0, reviewerProc: null, reviewerTimer: null,
|
|
174
176
|
reviewCounter: 0,
|
|
175
|
-
persistentReviewerSession: null, persistentReviewerKill: null, persistentReviewerSignalNum: 0,
|
|
177
|
+
persistentReviewerSession: null, persistentReviewerKill: null, persistentReviewerSignalNum: 0, reviewerRespawnCount: 0,
|
|
176
178
|
totalIterations: 0, stepStatuses: new Map(),
|
|
177
179
|
};
|
|
178
180
|
}
|
|
@@ -2503,9 +2505,23 @@ export default function (pi: ExtensionAPI) {
|
|
|
2503
2505
|
|
|
2504
2506
|
if (needsSpawn && state.persistentReviewerSession) {
|
|
2505
2507
|
// Session was previously active but died — log fallback
|
|
2506
|
-
|
|
2508
|
+
state.reviewerRespawnCount++;
|
|
2509
|
+
const MAX_REVIEWER_RESPAWNS = 3;
|
|
2510
|
+
if (state.reviewerRespawnCount > MAX_REVIEWER_RESPAWNS) {
|
|
2511
|
+
console.error(`[task-runner] reviewer respawn limit (${MAX_REVIEWER_RESPAWNS}) exceeded — skipping review`);
|
|
2512
|
+
logExecution(statusPath, `Reviewer R${num}`,
|
|
2513
|
+
`reviewer respawn limit exceeded (${state.reviewerRespawnCount}/${MAX_REVIEWER_RESPAWNS}) — skipping review`);
|
|
2514
|
+
state.persistentReviewerSession = null;
|
|
2515
|
+
state.persistentReviewerKill = null;
|
|
2516
|
+
state.persistentReviewerSignalNum = 0;
|
|
2517
|
+
return {
|
|
2518
|
+
content: [{ type: "text" as const, text: `⚠️ Reviewer respawn limit exceeded (${MAX_REVIEWER_RESPAWNS}). Review skipped — proceeding without review.` }],
|
|
2519
|
+
details: undefined,
|
|
2520
|
+
};
|
|
2521
|
+
}
|
|
2522
|
+
console.error(`[task-runner] persistent reviewer session dead — respawning (${state.reviewerRespawnCount}/${MAX_REVIEWER_RESPAWNS})`);
|
|
2507
2523
|
logExecution(statusPath, `Reviewer R${num}`,
|
|
2508
|
-
`persistent reviewer dead — respawning for ${reviewType} review`);
|
|
2524
|
+
`persistent reviewer dead — respawning for ${reviewType} review (${state.reviewerRespawnCount}/${MAX_REVIEWER_RESPAWNS})`);
|
|
2509
2525
|
state.persistentReviewerSession = null;
|
|
2510
2526
|
state.persistentReviewerKill = null;
|
|
2511
2527
|
state.persistentReviewerSignalNum = 0;
|
|
@@ -2553,7 +2569,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
2553
2569
|
};
|
|
2554
2570
|
} catch (err: any) {
|
|
2555
2571
|
// ── Fallback: kill persistent session, try fresh spawn ──
|
|
2556
|
-
|
|
2572
|
+
state.reviewerRespawnCount++;
|
|
2573
|
+
console.error(`[task-runner] persistent reviewer error (${state.reviewerRespawnCount}/3): ${err?.message || err}`);
|
|
2557
2574
|
logExecution(statusPath, `Reviewer R${num}`,
|
|
2558
2575
|
`persistent reviewer failed — falling back to fresh spawn: ${err?.message || err}`);
|
|
2559
2576
|
|
|
@@ -2565,6 +2582,17 @@ export default function (pi: ExtensionAPI) {
|
|
|
2565
2582
|
state.persistentReviewerKill = null;
|
|
2566
2583
|
state.persistentReviewerSignalNum = 0;
|
|
2567
2584
|
|
|
2585
|
+
// Circuit breaker — skip review if we've exhausted respawns
|
|
2586
|
+
if (state.reviewerRespawnCount > 3) {
|
|
2587
|
+
console.error(`[task-runner] reviewer respawn limit exceeded — skipping review`);
|
|
2588
|
+
logExecution(statusPath, `Reviewer R${num}`,
|
|
2589
|
+
`reviewer respawn limit exceeded — review skipped`);
|
|
2590
|
+
return {
|
|
2591
|
+
content: [{ type: "text" as const, text: `⚠️ Reviewer respawn limit exceeded. Review skipped — proceeding without review.` }],
|
|
2592
|
+
details: undefined,
|
|
2593
|
+
};
|
|
2594
|
+
}
|
|
2595
|
+
|
|
2568
2596
|
// ── Fresh spawn fallback (original behavior) ────────
|
|
2569
2597
|
try {
|
|
2570
2598
|
const promptContent = readFileSync(requestPath, "utf-8");
|
|
@@ -2945,8 +2973,35 @@ export default function (pi: ExtensionAPI) {
|
|
|
2945
2973
|
return;
|
|
2946
2974
|
}
|
|
2947
2975
|
} else {
|
|
2948
|
-
// ──
|
|
2949
|
-
//
|
|
2976
|
+
// ── Empty completion guard ────────────────────────────────
|
|
2977
|
+
// Detect tasks where the worker checked off STATUS.md without
|
|
2978
|
+
// modifying any source files. This catches "shortcut" completions
|
|
2979
|
+
// where the worker concludes work is "already done" without
|
|
2980
|
+
// implementing anything.
|
|
2981
|
+
if (isOrchestratedMode()) {
|
|
2982
|
+
try {
|
|
2983
|
+
const diffResult = spawnSync("git", ["diff", "--name-only", "HEAD"], {
|
|
2984
|
+
cwd: task.taskFolder, encoding: "utf-8", timeout: 10_000,
|
|
2985
|
+
});
|
|
2986
|
+
const changedFiles = (diffResult.stdout || "").split("\n").filter(Boolean);
|
|
2987
|
+
const sourceChanges = changedFiles.filter(f =>
|
|
2988
|
+
!f.endsWith("STATUS.md") && !f.endsWith(".DONE") &&
|
|
2989
|
+
!f.includes(".reviews/") && !f.endsWith("dependencies.json")
|
|
2990
|
+
);
|
|
2991
|
+
if (sourceChanges.length === 0) {
|
|
2992
|
+
logExecution(statusPath, "⚠️ Empty completion",
|
|
2993
|
+
"Worker marked all steps complete but no source files were modified. " +
|
|
2994
|
+
"Only STATUS.md changes detected. This may indicate the worker shortcut " +
|
|
2995
|
+
"the task without implementing. .DONE will still be created, but this " +
|
|
2996
|
+
"should be investigated.");
|
|
2997
|
+
console.error(`[task-runner] WARNING: Task ${task.taskId} completed with zero source file changes`);
|
|
2998
|
+
}
|
|
2999
|
+
} catch {
|
|
3000
|
+
// Best effort — don't block .DONE creation on git check failure
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
|
|
3004
|
+
// Create .DONE
|
|
2950
3005
|
const donePath = join(task.taskFolder, ".DONE");
|
|
2951
3006
|
writeFileSync(donePath, `Completed: ${new Date().toISOString()}\nTask: ${task.taskId}\n`);
|
|
2952
3007
|
updateStatusField(statusPath, "Status", "✅ Complete");
|
package/package.json
CHANGED
|
@@ -354,6 +354,30 @@ files and make sure their file scopes reflect that.
|
|
|
354
354
|
|
|
355
355
|
---
|
|
356
356
|
|
|
357
|
+
## Preventing Empty Completions
|
|
358
|
+
|
|
359
|
+
Workers can shortcut tasks by observing that existing code "already satisfies"
|
|
360
|
+
requirements and checking off items without implementing anything. This is the
|
|
361
|
+
most dangerous failure mode — it produces false completions that waste the entire
|
|
362
|
+
pipeline.
|
|
363
|
+
|
|
364
|
+
**Defense: Make deliverables concrete and verifiable.**
|
|
365
|
+
|
|
366
|
+
| ❌ Vague (shortcuttable) | ✅ Concrete (verifiable) |
|
|
367
|
+
|--------------------------|------------------------|
|
|
368
|
+
| "Add taskPacketRepo support" | "Add `taskPacketRepo` field to `WorkspaceRoutingConfig` in types.ts" |
|
|
369
|
+
| "Enforce mode selection" | "Add `validateWorkspaceMode()` function in workspace.ts that throws on invalid state" |
|
|
370
|
+
| "Update config loading" | "Modify `loadWorkspaceConfig()` to parse and validate `taskPacketRepo` from JSON config" |
|
|
371
|
+
| "Add tests" | "Create `tests/packet-home-contract.test.ts` with tests for: valid config, missing field error, invariant violation" |
|
|
372
|
+
|
|
373
|
+
**Rules for task creators:**
|
|
374
|
+
- Every implementation step MUST name specific files to create or modify
|
|
375
|
+
- "Add X" means "write new code that doesn't exist yet" — if it might already exist, say "verify X exists and add tests, or implement if missing"
|
|
376
|
+
- Include at least one NEW test file per task — workers can't shortcut test creation
|
|
377
|
+
- Each step's artifacts list must include at least one source file (not just STATUS.md)
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
357
381
|
## Key Principles
|
|
358
382
|
|
|
359
383
|
- **Documentation in every task.** Without "Must Update" and "Check If Affected"
|
|
@@ -173,6 +173,15 @@ When a reviewer returns REVISE with specific feedback items:
|
|
|
173
173
|
- Do NOT expand task scope beyond what the steps require
|
|
174
174
|
- If you discover something out of scope, note it in STATUS.md Discoveries table
|
|
175
175
|
|
|
176
|
+
## Completion Integrity
|
|
177
|
+
|
|
178
|
+
**Every checked checkbox MUST correspond to a real code change, test, or document edit.** You must NOT check off items by simply observing that existing code appears to satisfy them. Specifically:
|
|
179
|
+
|
|
180
|
+
- **If you believe work is already done:** You must still verify by running tests against the specific requirements AND document what you verified. Check off the item only after confirming with evidence (test output, code inspection notes in STATUS.md).
|
|
181
|
+
- **"No source files changed" is a red flag.** If you complete a task without modifying any source files (only STATUS.md), something is wrong. Every implementation task requires code changes. If you genuinely believe no changes are needed, log a detailed explanation in STATUS.md Discoveries and escalate — do NOT mark the task as complete.
|
|
182
|
+
- **A step that requires "Add X to Y" means you write the code.** Reading existing code and deciding it already satisfies the requirement is not implementation. If the existing code truly covers it, write a test that proves it, and document the finding.
|
|
183
|
+
- **Checking boxes without doing work is the most serious failure mode.** It wastes the entire batch pipeline (review, merge, integration) and produces a false completion that blocks dependent tasks.
|
|
184
|
+
|
|
176
185
|
## Review Protocol
|
|
177
186
|
|
|
178
187
|
If you have access to a `review_step` tool, use it at step boundaries to spawn
|