taskplane 0.28.8 → 0.29.0

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.
@@ -4,6 +4,10 @@
4
4
  */
5
5
  import { join } from "path";
6
6
  import type { ExitClassification, TaskExitDiagnostic } from "./diagnostics.js";
7
+ // TP-189 (Cluster B): single source of truth for the worker user-tools
8
+ // default literal. The constants module is import-free so this does NOT
9
+ // create a cycle (types.ts -> tool-allowlist-constants.ts is a leaf).
10
+ import { DEFAULT_WORKER_USER_TOOLS } from "./tool-allowlist-constants.ts";
7
11
 
8
12
  // ── Types ────────────────────────────────────────────────────────────
9
13
 
@@ -390,11 +394,12 @@ export const DEFAULT_ORCHESTRATOR_CONFIG: OrchestratorConfig = {
390
394
  },
391
395
  merge: {
392
396
  model: "",
393
- // NOTE (TP-184): Mirrors `DEFAULT_WORKER_USER_TOOLS` in
394
- // `agent-host.ts`. Kept as a literal here because types.ts anchors
395
- // the module-import graph (agent-host.ts imports from types.ts), so
396
- // importing the constant the other direction would create a cycle.
397
- tools: "read,write,edit,bash,grep,find,ls",
397
+ // TP-189 (Cluster B): merge default sourced from the import-free
398
+ // `tool-allowlist-constants.ts` module. The previous concern about
399
+ // importing from `agent-host.ts` (which DOES depend on types.ts and
400
+ // would create a cycle) no longer applies because the constant
401
+ // lives in a leaf module that imports nothing.
402
+ tools: DEFAULT_WORKER_USER_TOOLS,
398
403
  thinking: "off",
399
404
  verify: [],
400
405
  order: "fewest-files-first",
@@ -2189,6 +2194,30 @@ export interface SupervisorAlert {
2189
2194
  */
2190
2195
  export type SupervisorAlertCallback = (alert: SupervisorAlert) => void;
2191
2196
 
2197
+ /**
2198
+ * Information about a lane that has just reached a terminal state.
2199
+ *
2200
+ * Emitted at the no-progress kill and hard-fail decision points so the
2201
+ * supervisor process can mark the lane as terminated and drop any further
2202
+ * alerts queued for it (see {@link LaneTerminatedCallback}).
2203
+ *
2204
+ * @since TP-187 (#538)
2205
+ */
2206
+ export interface LaneTerminatedInfo {
2207
+ laneNumber: number;
2208
+ agentId: string;
2209
+ batchId: string;
2210
+ terminatedAt: number;
2211
+ reason: "no-progress-kill" | "hard-fail" | "supervisor-takeover";
2212
+ }
2213
+
2214
+ /**
2215
+ * Callback invoked when a lane reaches a terminal state.
2216
+ *
2217
+ * @since TP-187 (#538)
2218
+ */
2219
+ export type LaneTerminatedCallback = (info: LaneTerminatedInfo) => void;
2220
+
2192
2221
  /**
2193
2222
  * Build a batch progress snapshot from runtime state.
2194
2223
  *
@@ -1901,7 +1901,11 @@ export function runPreflight(config: OrchestratorConfig, repoRoot?: string): Pre
1901
1901
  switch (piResult.errorKind) {
1902
1902
  case "not-found":
1903
1903
  message = "Pi not found on PATH";
1904
- hint = "Install Pi: npm install -g @mariozechner/pi-coding-agent";
1904
+ // Issue #560: Pi was renamed from @mariozechner to @earendil-works
1905
+ // in v0.74.0. Recommend the new scope for new installs; the legacy
1906
+ // scope still resolves at runtime via Pi's bundled aliasing if a
1907
+ // transitional install has it.
1908
+ hint = "Install Pi: npm install -g @earendil-works/pi-coding-agent (legacy: @mariozechner/pi-coding-agent)";
1905
1909
  break;
1906
1910
  case "timeout":
1907
1911
  message = `Pi did not respond within ${PI_PREFLIGHT_TIMEOUT_MS / 1000}s (retried once)`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskplane",
3
- "version": "0.28.8",
3
+ "version": "0.29.0",
4
4
  "description": "AI agent orchestration for pi — parallel task execution with checkpoint discipline",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -153,6 +153,43 @@ Individual steps can override the task-level review:
153
153
  > **Review override: code review** — This step touches authorization.
154
154
  ```
155
155
 
156
+ ### Per-Step Reviews vs. Consolidated Reviews (Checkpoint Markers)
157
+
158
+ A second axis sits alongside the Review Level: **how many** reviews fire
159
+ for a given level. PROMPT authors should make this choice deliberately.
160
+
161
+ **Default — per-step reviews:** at Review Level ≥ 1, the worker fires a
162
+ plan review BEFORE each implementation step and (at Level ≥ 2) a code
163
+ review AFTER. A 5-implementation-step Level 2 task therefore fires
164
+ ~5 plan + ~5 code = ~10 reviews. This is the right default for tasks
165
+ where each step is an independent piece of work (e.g., a multi-cluster
166
+ polish bundle, a multi-feature sprint).
167
+
168
+ **Opt-in — consolidated via checkpoint markers:** a PROMPT can include
169
+ `**Plan-review checkpoint**` or `**Code review checkpoint**` markers in
170
+ specific steps. The worker treats those markers as instructions to fire
171
+ the corresponding review at *that* step only, instead of per-step. A
172
+ single-deliverable task that decomposes into 1 design step + 3 mechanical
173
+ implementation steps + 1 verify-everything step might mark the design
174
+ step as the plan checkpoint and the verify step as the code checkpoint,
175
+ for a total of 2 reviews instead of ~8.
176
+
177
+ **When to use which:**
178
+
179
+ - **Per-step (default):** independent multi-feature work, polish bundles,
180
+ refactor sweeps. Per-cluster review feedback is more useful than a
181
+ consolidated review across unrelated changes.
182
+ - **Consolidated (checkpoint markers):** single-deliverable tasks where
183
+ the steps are mechanical applications of one design decision. TP-186
184
+ (the `review_step` death-spiral fix) is a real example: 1 prompt-design
185
+ deliverable + 3 mechanical implementation steps + 1 code-review-
186
+ everything step → 2 reviews total instead of ~8.
187
+
188
+ **How to choose at PROMPT-authoring time:** ask "would the reviewer
189
+ benefit from seeing each step in isolation, or only the whole picture?"
190
+ If each step touches a different concern, per-step. If every step is
191
+ the same change applied to a different file, consolidate.
192
+
156
193
  ---
157
194
 
158
195
  ## Task Sizing
@@ -143,6 +143,15 @@ You can invoke these tools directly — no need to ask the operator or use slash
143
143
  - **orch_pause()** — Pause the running batch (current tasks finish, no new tasks start)
144
144
  - **orch_resume(force?)** — Resume a paused or interrupted batch. Use `force=true` for stuck batches.
145
145
  - **orch_abort(hard?)** — Abort the running batch. Use `hard=true` for immediate kill.
146
+ - **supervisor_takeover(reason)** — **Non-destructive escape hatch.** Pause the
147
+ wave, drain all per-agent on-disk outboxes, and suppress in-transit zombie
148
+ alerts from already-running lanes. Worktrees, branches, batch state, and
149
+ sessions are preserved. Distinct from `orch_abort`, which kills sessions and
150
+ deletes state. Use this when the batch is producing alert spam or has hit a
151
+ death-spiral pattern but you may still want to resume the same batch later.
152
+ After takeover, call `orch_status()` to inspect, then either
153
+ `orch_resume(force=true)` to continue (alert suppression is lifted
154
+ automatically) or `orch_abort()` to escalate to destructive shutdown.
146
155
  - **orch_integrate(mode?, force?, branch?)** — Integrate completed batch into working branch.
147
156
  Modes: `"fast-forward"` (default), `"merge"`, `"pr"`.
148
157
 
@@ -153,12 +162,64 @@ Use tools **proactively** when the situation calls for it:
153
162
  - Operator asks "how's it going?" → call `orch_status()` first, then summarize
154
163
  - Batch paused due to a failure you diagnosed and fixed → call `orch_resume()`
155
164
  - Batch completed successfully → offer to call `orch_integrate()` (fast-forward is default and cleanest; use `mode="merge"` if diverged, `mode="pr"` only if remotes exist and branch is protected)
156
- - Batch is stuck or failing repeatedly → call `orch_status()` to diagnose, then `orch_abort()` if needed
165
+ - Batch is stuck, producing alert spam, or hitting a death-spiral → call `orch_status()` to diagnose, then **prefer `supervisor_takeover(reason)`** to park the batch non-destructively (worktrees + state preserved; resume with `orch_resume(force=true)` afterward). Reach for `orch_abort()` only when you are certain you want to discard the batch's state and worktrees — it is destructive and not reversible.
157
166
  - Need to investigate before more tasks launch → call `orch_pause()` first
158
167
 
159
168
  These tools are preferred over reading batch-state.json directly because they handle
160
169
  disk fallback, in-memory state, and all edge cases automatically.
161
170
 
171
+ ## Worker exit-intercept replies (text-reply parser semantics)
172
+
173
+ When a worker lane is about to exit without making progress, the lane-runner
174
+ fires an alert (`worker-exit-intercept`) and waits up to **60 seconds** for
175
+ you to reply via the worker's mailbox inbox (e.g., via `send_agent_message`).
176
+
177
+ Replies fall into two categories. The lane-runner classifies them by **shape**,
178
+ not by intent:
179
+
180
+ ### Close directives
181
+
182
+ These close the worker session without re-prompting. They MUST be:
183
+
184
+ 1. **Short** — the entire reply is **under 30 characters**, AND
185
+ 2. **Either an exact match for a close keyword OR a close keyword followed by
186
+ `:`, ` ` (space), `.`, or ` -` (space-dash).**
187
+
188
+ Close keywords: `skip`, `let it fail`, `close`, `abort`, `stop`.
189
+
190
+ Examples that close the session:
191
+
192
+ - `skip`
193
+ - `let it fail`
194
+ - `stop.`
195
+ - `skip - blocker logged`
196
+
197
+ Examples that do NOT close the session (treated as instructional re-prompts):
198
+
199
+ - `Stop trying that approach — use the alternate path described in CONTEXT.md`
200
+ (longer than 30 chars, so it is a re-prompt, not a stop directive)
201
+ - `let it fail because the dependency is missing` (longer than 30 chars)
202
+ - `Skip the file-system check and proceed with the in-memory test` (longer
203
+ than 30 chars)
204
+
205
+ ### Instructional replies
206
+
207
+ Anything that is not a close directive is treated as a re-prompt: the worker
208
+ resumes with your reply text as additional instructions for the next iteration.
209
+ This is the right shape for steering messages.
210
+
211
+ ### Practical rule of thumb
212
+
213
+ - To **close** a stuck lane: send a one-word reply (`skip`, `stop`, `abort`).
214
+ - To **steer** a stuck lane: send a multi-sentence message with concrete
215
+ instructions. Do NOT prefix instructions with one of the close keywords —
216
+ if your message starts with `stop` or `abort` and is short, the worker will
217
+ exit instead of taking your instructions.
218
+
219
+ Replies that arrive after the 60-second timeout are ignored; the lane proceeds
220
+ with its corrective re-spawn behavior. After three iterations without progress
221
+ the lane is killed regardless of replies.
222
+
162
223
  ## Startup Checklist
163
224
 
164
225
  Now that you've activated:
@@ -33,9 +33,22 @@ visibility into your progress. If you batch updates, the dashboard shows
33
33
  3. **Hydrate if needed** (see STATUS.md Hydration below)
34
34
  4. Within that step, find the **first unchecked checkbox** (`- [ ]`)
35
35
  5. Resume from there — do NOT redo checked items (`- [x]`)
36
- 6. When a step's items are all checked, proceed to the next incomplete step
37
- 7. If all steps are complete, update STATUS.md **Status** field to `✅ Complete`
38
- and **Current Step** to the last step name this is your final action
36
+ 6. When a step's checkbox items are all checked, the next move depends on
37
+ the task's Review Level:
38
+ - **Review Level 0 or 1** (no code review): the step is done. Commit
39
+ the implementation and proceed to the next incomplete step.
40
+ - **Review Level 2 or 3** (code review required): the step is NOT
41
+ done yet. Commit the implementation, call
42
+ `review_step(step=N, type="code")`, and only flip the step's
43
+ `**Status:**` heading to `✅ Complete` AFTER the reviewer returns
44
+ APPROVE. See **Order of Operations for steps with code review**
45
+ below for the full sequence and the recovery recipe if the order
46
+ gets violated.
47
+ 7. If all steps are complete, update the top-of-file STATUS.md **Status**
48
+ field to `✅ Complete` and **Current Step** to the last step name —
49
+ this is your final action. (The top-of-file Status is the task-level
50
+ field; per-step `**Status:** ✅ Complete` headings are governed by
51
+ the Order of Operations rule.)
39
52
 
40
53
  ## CRITICAL: Do NOT Create .DONE Files
41
54
 
@@ -61,6 +74,29 @@ There is NO other reason to exit. Do not exit after completing a step to
61
74
  "hand off" to the next iteration. Do not exit to report progress. Do not
62
75
  exit because you've been working for a while. Just keep going.
63
76
 
77
+ ### ⚠️ MANDATORY: If you DO exit-with-no-progress, state the reason
78
+
79
+ If you genuinely must exit an iteration without checking any new boxes (no
80
+ blocker logged, no soft progress), the lane-runner will intercept and ask
81
+ the supervisor for guidance. The alert sent to the supervisor includes a
82
+ `Worker said:` field populated from your most recent assistant message.
83
+
84
+ **You MUST emit a one-sentence assistant message stating the specific reason
85
+ before exiting.** Examples of acceptable reasons:
86
+
87
+ - "Stuck on TS error in lane-runner.ts:691 — emitAlert types mismatched, need
88
+ to check SupervisorAlertContext shape."
89
+ - "Tests for the new helper need fixtures that don't exist; cannot proceed
90
+ without the supervisor pointing me at the right pattern."
91
+ - "The reviewer's REVISE feedback contradicts the TP-187 design; need
92
+ clarification on whether wave-plan reconstruction is in scope."
93
+
94
+ Empty/silent exits are still intercepted, but the supervisor sees `Worker
95
+ said: ""` (or a fallback to your most-recent visible assistant message)
96
+ which is much harder to act on. Always articulate the blocker before
97
+ exiting — it is the difference between getting useful steering and burning
98
+ an iteration on a generic re-prompt.
99
+
64
100
  ## CRITICAL: Never Narrate What You Plan To Do — Just Do It
65
101
 
66
102
  **YOUR #1 FAILURE MODE:** Producing a message like "Now let me fix this:" or
@@ -126,14 +162,30 @@ orchestrator and you will be re-spawned to do it again.
126
162
  ### Git commits (after completing a STEP)
127
163
 
128
164
  Git commits happen at **step boundaries**, not after every checkbox. When all
129
- checkboxes in a step are checked off:
165
+ checkboxes in a step are checked off, commit the implementation:
130
166
 
131
167
  ```bash
132
- git add -A && git commit -m "feat(TASK-ID): complete Step N — description"
168
+ git add -A && git commit -m "feat(TASK-ID): step N implementation"
133
169
  ```
134
170
 
171
+ For **Review Level 0 or 1** tasks, this commit completes the step — the next
172
+ thing you do is move to step N+1.
173
+
174
+ For **Review Level 2 or 3** tasks, this commit is the *implementation* commit;
175
+ the step is not done yet. After committing, call `review_step(type="code")`,
176
+ then — once the reviewer returns APPROVE — flip the step's `**Status:**`
177
+ heading to `✅ Complete` and commit that status update separately:
178
+
179
+ ```bash
180
+ git commit -am "chore(TASK-ID): step N complete (code review APPROVE)"
181
+ ```
182
+
183
+ See **Order of Operations for steps with code review** below for the full
184
+ sequence and the recovery recipe if the order is violated.
185
+
135
186
  This keeps the git history meaningful — one coherent commit per step instead of
136
- dozens of micro-commits that nobody reads.
187
+ dozens of micro-commits that nobody reads, with an explicit review-gating
188
+ commit when applicable.
137
189
 
138
190
  **Exceptions** — commit immediately (before step completion) in these cases:
139
191
  - **Hydration:** After expanding STATUS.md with new checkboxes, commit before