taskplane 0.22.5 → 0.22.7

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.
@@ -2148,6 +2148,9 @@ export default function (pi: ExtensionAPI) {
2148
2148
 
2149
2149
  // ── review_step Tool (orchestrated mode only) ───────────────────
2150
2150
 
2151
+ /** Per-step code review cycle counter. Reset when reviewer is killed after APPROVE. */
2152
+ const stepCodeReviewCounts = new Map<number, number>();
2153
+
2151
2154
  /**
2152
2155
  * Reset reviewer telemetry fields on state to idle/zero.
2153
2156
  * Called after a review completes to clear dashboard metrics.
@@ -2291,6 +2294,30 @@ export default function (pi: ExtensionAPI) {
2291
2294
  const reviewsDir = join(task.taskFolder, ".reviews");
2292
2295
  if (!existsSync(reviewsDir)) mkdirSync(reviewsDir, { recursive: true });
2293
2296
 
2297
+ // Per-step code review cycle limit
2298
+ if (reviewType === "code") {
2299
+ const codeCount = (stepCodeReviewCounts.get(stepNum) || 0) + 1;
2300
+ stepCodeReviewCounts.set(stepNum, codeCount);
2301
+ const maxCycles = config.context.max_review_cycles || 2;
2302
+ if (codeCount > maxCycles) {
2303
+ logExecution(statusPath, `Skip code review`,
2304
+ `Step ${stepNum} code review cycle limit reached (${codeCount}/${maxCycles}) — auto-approving`);
2305
+ // Kill reviewer to free context for next step
2306
+ if (state.persistentReviewerKill) {
2307
+ try { state.persistentReviewerKill(); } catch {}
2308
+ }
2309
+ state.persistentReviewerSession = null;
2310
+ state.persistentReviewerKill = null;
2311
+ state.persistentReviewerSignalNum = 0;
2312
+ state.reviewerRespawnCount = 0;
2313
+ stepCodeReviewCounts.delete(stepNum);
2314
+ return {
2315
+ content: [{ type: "text" as const, text: `APPROVE — Code review cycle limit reached (${maxCycles}). Auto-approved to prevent context exhaustion.` }],
2316
+ details: undefined,
2317
+ };
2318
+ }
2319
+ }
2320
+
2294
2321
  // Low-risk step check (safety net — worker template also skips)
2295
2322
  if (isLowRiskStep(stepNum, task.steps.length)) {
2296
2323
  const label = stepNum === 0 ? "Preflight" : "final step";
@@ -2551,11 +2578,29 @@ export default function (pi: ExtensionAPI) {
2551
2578
  updateWidgets();
2552
2579
 
2553
2580
  // Extract verdict and build result
2554
- const { resultText } = processReviewVerdict(
2581
+ const { resultText, verdict } = processReviewVerdict(
2555
2582
  reviewContent, statusPath, num, reviewType, stepNum, state.reviewCounter,
2556
2583
  );
2557
2584
 
2558
- // Set reviewer to idle (NOT clear persistent session stays alive)
2585
+ // After code review APPROVE: kill the persistent reviewer to free context.
2586
+ // The reviewer persists through plan+code for one step, and through
2587
+ // REVISE→fix→re-review cycles (it knows what it asked to be fixed).
2588
+ // Only kill on APPROVE — a REVISE means the worker needs to fix and
2589
+ // re-submit, and the same reviewer should evaluate the follow-up.
2590
+ if (reviewType === "code" && (verdict === "APPROVE" || verdict === "UNAVAILABLE")) {
2591
+ console.error(`[task-runner] code review ${verdict} for step ${stepNum} — killing reviewer for fresh context on next step`);
2592
+ logExecution(statusPath, `Reviewer R${num}`,
2593
+ `code review ${verdict} — killing persistent reviewer (step ${stepNum} cycle done)`);
2594
+ if (state.persistentReviewerKill) {
2595
+ try { state.persistentReviewerKill(); } catch {}
2596
+ }
2597
+ state.persistentReviewerSession = null;
2598
+ state.persistentReviewerKill = null;
2599
+ state.persistentReviewerSignalNum = 0;
2600
+ state.reviewerRespawnCount = 0;
2601
+ stepCodeReviewCounts.delete(stepNum);
2602
+ }
2603
+
2559
2604
  state.reviewerStatus = "idle";
2560
2605
  state.reviewerType = "";
2561
2606
  state.reviewerStep = 0;
@@ -2643,6 +2688,9 @@ export default function (pi: ExtensionAPI) {
2643
2688
  fallbackContent, statusPath, num, reviewType, stepNum, state.reviewCounter, "fallback",
2644
2689
  );
2645
2690
 
2691
+ // Reset respawn counter on successful fallback review
2692
+ state.reviewerRespawnCount = 0;
2693
+
2646
2694
  clearReviewerState();
2647
2695
  writeLaneState(state);
2648
2696
  updateWidgets();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskplane",
3
- "version": "0.22.5",
3
+ "version": "0.22.7",
4
4
  "description": "AI agent orchestration for pi — parallel task execution with checkpoint discipline",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -289,33 +289,35 @@ checkpoints protect against regressions even when intermediate steps use targete
289
289
  2. The merge agent (before merging to the orchestrator branch)
290
290
  3. CI (before merging to main)
291
291
 
292
- ## File Reading Strategy (Context Budget)
292
+ ## File Reading Strategy (Context Budget) — CRITICAL
293
293
 
294
- Your context window is finite. Reading large files whole wastes budget and risks
295
- triggering the context-pressure safety net (85% wrap-up, 95% → kill).
296
- Use targeted reads instead:
294
+ Your context window is finite. **Reading large files without offset/limit is the
295
+ #1 cause of context exhaustion** one full read of a 3000-line file consumes
296
+ ~5% of a 1M context window. Three such reads = 15% gone before you've done
297
+ anything.
298
+
299
+ ### HARD RULES
300
+
301
+ 1. **NEVER read a file > 500 lines without offset/limit.** Always grep first.
302
+ 2. **NEVER read the same file twice in full.** Re-read only the changed region.
303
+ 3. **ALWAYS check file size before reading:** `wc -l <file>` or `ls -la <file>`
297
304
 
298
305
  ### Pattern: grep-first, read-with-offset
299
306
 
300
- 1. **Locate** the relevant section with `grep` or `find`:
301
- ```
302
- grep -n "function buildPrompt" extensions/task-runner.ts
303
- ```
304
- 2. **Read** just that region with `offset` and `limit`:
305
- ```
306
- read extensions/task-runner.ts (offset: 1773, limit: 50)
307
- ```
308
- 3. **Edit** surgically with exact `oldText → newText`
307
+ 1. **Check size:** `wc -l extensions/task-runner.ts` 4100 lines (DO NOT read fully)
308
+ 2. **Locate** the relevant section: `grep -n "function buildPrompt" extensions/task-runner.ts`
309
+ 3. **Read** just that region: `read extensions/task-runner.ts (offset: 1773, limit: 50)`
310
+ 4. **Edit** surgically with exact `oldText → newText`
309
311
 
310
312
  ### When to read a full file
311
313
 
312
314
  - Files under ~500 lines — read the whole thing, it's fine
313
- - Config files, test files, templates — usually small enough to read fully
315
+ - Config files, small test files, templates — usually small enough
314
316
  - New files you're creating — read after writing to verify
315
317
 
316
318
  ### When NOT to read a full file
317
319
 
318
- - Source files over ~1000 lines — grep first, read the relevant region
320
+ - Source files over ~500 lines — grep first, read with offset/limit
319
321
  - Generated files, lock files, large data files — almost never need full reads
320
322
  - Files you've already read this session — re-read only the changed region
321
323