@agent-plan/core 0.2.26 → 0.2.28

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.
Files changed (47) hide show
  1. package/dist/description-freshness.d.ts +37 -0
  2. package/dist/description-freshness.d.ts.map +1 -0
  3. package/dist/description-freshness.js +84 -0
  4. package/dist/handoff-context.d.ts +135 -3
  5. package/dist/handoff-context.d.ts.map +1 -1
  6. package/dist/handoff-context.js +336 -25
  7. package/dist/index.d.ts +4 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +4 -1
  10. package/dist/package-version.d.ts +2 -0
  11. package/dist/package-version.d.ts.map +1 -1
  12. package/dist/package-version.js +1 -1
  13. package/dist/payload-fallback.d.ts +1 -1
  14. package/dist/payload-fallback.d.ts.map +1 -1
  15. package/dist/payload-fallback.js +6 -0
  16. package/dist/plan-store.d.ts +129 -30
  17. package/dist/plan-store.d.ts.map +1 -1
  18. package/dist/plan-store.js +690 -115
  19. package/dist/planner-rules.d.ts.map +1 -1
  20. package/dist/planner-rules.js +6 -2
  21. package/dist/read-tracking.d.ts +27 -3
  22. package/dist/read-tracking.d.ts.map +1 -1
  23. package/dist/read-tracking.js +53 -4
  24. package/dist/recap.d.ts.map +1 -1
  25. package/dist/recap.js +33 -9
  26. package/dist/renderer.d.ts.map +1 -1
  27. package/dist/renderer.js +0 -1
  28. package/dist/requirement-macro-tasks.d.ts +2 -2
  29. package/dist/requirement-macro-tasks.d.ts.map +1 -1
  30. package/dist/requirement-macro-tasks.js +2 -2
  31. package/dist/runtime-diagnostics.d.ts +34 -0
  32. package/dist/runtime-diagnostics.d.ts.map +1 -0
  33. package/dist/runtime-diagnostics.js +39 -0
  34. package/dist/schema.d.ts +487 -17
  35. package/dist/schema.d.ts.map +1 -1
  36. package/dist/schema.js +24 -4
  37. package/dist/task-context.d.ts +41 -2
  38. package/dist/task-context.d.ts.map +1 -1
  39. package/dist/task-context.js +102 -4
  40. package/dist/task-selection.d.ts +44 -1
  41. package/dist/task-selection.d.ts.map +1 -1
  42. package/dist/task-selection.js +158 -7
  43. package/dist/write-coordination.d.ts +27 -0
  44. package/dist/write-coordination.d.ts.map +1 -0
  45. package/dist/write-coordination.js +223 -0
  46. package/package.json +1 -1
  47. package/planner-skill.md +36 -20
@@ -0,0 +1,223 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { mkdir, readFile, rm, stat, utimes, writeFile } from "node:fs/promises";
3
+ import { hostname } from "node:os";
4
+ import { join, resolve } from "node:path";
5
+ import { AsyncLocalStorage } from "node:async_hooks";
6
+ const DEFAULT_STALE_MS = 30_000;
7
+ const DEFAULT_TIMEOUT_MS = 5_000;
8
+ const DEFAULT_RETRY_MS = 20;
9
+ export class PlanWriterBusyError extends Error {
10
+ details;
11
+ code = "PLAN_WRITER_BUSY";
12
+ constructor(details) {
13
+ const owner = details.owner
14
+ ? ` Active writer: pid ${details.owner.pid} on ${details.owner.hostname}, acquired ${details.owner.acquiredAt}.`
15
+ : "";
16
+ super(`PLAN_WRITER_BUSY: another process is mutating ${details.planRoot}; waited ${details.waitedMs}ms.${owner} Read-only operations remain available; retry the write after the active mutation finishes.`);
17
+ this.details = details;
18
+ this.name = "PlanWriterBusyError";
19
+ }
20
+ }
21
+ const heldRoots = new AsyncLocalStorage();
22
+ function positiveEnvMs(name, fallback) {
23
+ const raw = Number.parseInt(process.env[name] ?? "", 10);
24
+ return Number.isFinite(raw) && raw > 0 ? raw : fallback;
25
+ }
26
+ async function readOwner(lockPath) {
27
+ try {
28
+ const parsed = JSON.parse(await readFile(join(lockPath, "owner.json"), "utf8"));
29
+ if (typeof parsed.token !== "string"
30
+ || typeof parsed.pid !== "number"
31
+ || typeof parsed.hostname !== "string"
32
+ || typeof parsed.cwd !== "string"
33
+ || typeof parsed.acquiredAt !== "string")
34
+ return undefined;
35
+ return parsed;
36
+ }
37
+ catch {
38
+ return undefined;
39
+ }
40
+ }
41
+ function ownerIsActive(owner, heartbeatStale) {
42
+ if (owner.hostname !== hostname())
43
+ return !heartbeatStale;
44
+ try {
45
+ process.kill(owner.pid, 0);
46
+ return true;
47
+ }
48
+ catch (error) {
49
+ return error.code === "EPERM";
50
+ }
51
+ }
52
+ async function pathExists(path) {
53
+ try {
54
+ await stat(path);
55
+ return true;
56
+ }
57
+ catch {
58
+ return false;
59
+ }
60
+ }
61
+ async function acquirePlanRootLock(planRoot) {
62
+ const locksRoot = join(planRoot, ".local", "locks");
63
+ const lockPath = join(locksRoot, "writer.lock");
64
+ const recoveryPath = join(locksRoot, "writer-recovery.lock");
65
+ const startedAt = Date.now();
66
+ const timeoutMs = positiveEnvMs("AGENT_PLAN_WRITE_LOCK_TIMEOUT_MS", DEFAULT_TIMEOUT_MS);
67
+ const staleMs = positiveEnvMs("AGENT_PLAN_WRITE_LOCK_STALE_MS", DEFAULT_STALE_MS);
68
+ const retryMs = positiveEnvMs("AGENT_PLAN_WRITE_LOCK_RETRY_MS", DEFAULT_RETRY_MS);
69
+ const owner = {
70
+ token: randomUUID(),
71
+ pid: process.pid,
72
+ hostname: hostname(),
73
+ cwd: process.cwd(),
74
+ acquiredAt: new Date().toISOString(),
75
+ };
76
+ for (;;) {
77
+ try {
78
+ await mkdir(locksRoot, { recursive: true });
79
+ }
80
+ catch (error) {
81
+ if (error.code !== "ENOENT")
82
+ throw error;
83
+ await new Promise((resolveDelay) => setTimeout(resolveDelay, retryMs));
84
+ continue;
85
+ }
86
+ if (await pathExists(recoveryPath)) {
87
+ try {
88
+ const recovery = await stat(recoveryPath);
89
+ if (Date.now() - recovery.mtimeMs > staleMs) {
90
+ await rm(recoveryPath, { recursive: true, force: true });
91
+ continue;
92
+ }
93
+ }
94
+ catch {
95
+ continue;
96
+ }
97
+ const waitedMs = Date.now() - startedAt;
98
+ if (waitedMs >= timeoutMs) {
99
+ const currentOwner = await readOwner(lockPath);
100
+ throw new PlanWriterBusyError({
101
+ errorCode: "PLAN_WRITER_BUSY",
102
+ planRoot,
103
+ lockPath,
104
+ waitedMs,
105
+ ...(currentOwner ? { owner: currentOwner } : {}),
106
+ });
107
+ }
108
+ await new Promise((resolveDelay) => setTimeout(resolveDelay, retryMs));
109
+ continue;
110
+ }
111
+ try {
112
+ await mkdir(lockPath);
113
+ try {
114
+ await writeFile(join(lockPath, "owner.json"), JSON.stringify(owner, null, 2), "utf8");
115
+ // A stale-owner recovery may have started after our initial check. Its
116
+ // sentinel wins: withdraw this new lock and retry after recovery ends.
117
+ if (await pathExists(recoveryPath)) {
118
+ const persistedOwner = await readOwner(lockPath);
119
+ if (persistedOwner?.token === owner.token) {
120
+ await rm(lockPath, { recursive: true, force: true }).catch(() => { });
121
+ }
122
+ await new Promise((resolveDelay) => setTimeout(resolveDelay, retryMs));
123
+ continue;
124
+ }
125
+ }
126
+ catch (error) {
127
+ const persistedOwner = await readOwner(lockPath);
128
+ if (!persistedOwner || persistedOwner.token === owner.token) {
129
+ await rm(lockPath, { recursive: true, force: true }).catch(() => { });
130
+ }
131
+ throw error;
132
+ }
133
+ const heartbeat = setInterval(() => {
134
+ const now = new Date();
135
+ void utimes(lockPath, now, now).catch(() => { });
136
+ }, Math.max(250, Math.floor(staleMs / 3)));
137
+ heartbeat.unref();
138
+ return async () => {
139
+ clearInterval(heartbeat);
140
+ const persistedOwner = await readOwner(lockPath);
141
+ if (persistedOwner?.token === owner.token) {
142
+ await rm(lockPath, { recursive: true, force: true }).catch(() => { });
143
+ }
144
+ };
145
+ }
146
+ catch (error) {
147
+ const fsError = error;
148
+ if (fsError.code === "ENOENT") {
149
+ await new Promise((resolveDelay) => setTimeout(resolveDelay, retryMs));
150
+ continue;
151
+ }
152
+ if (fsError.code !== "EEXIST")
153
+ throw error;
154
+ const currentOwner = await readOwner(lockPath);
155
+ let stale = false;
156
+ try {
157
+ const info = await stat(lockPath);
158
+ stale = Date.now() - info.mtimeMs > staleMs;
159
+ }
160
+ catch {
161
+ continue;
162
+ }
163
+ if (currentOwner ? !ownerIsActive(currentOwner, stale) : stale) {
164
+ try {
165
+ await mkdir(recoveryPath);
166
+ try {
167
+ const confirmedOwner = await readOwner(lockPath);
168
+ let confirmedStale = false;
169
+ try {
170
+ const info = await stat(lockPath);
171
+ confirmedStale = Date.now() - info.mtimeMs > staleMs;
172
+ }
173
+ catch {
174
+ continue;
175
+ }
176
+ if (confirmedOwner ? !ownerIsActive(confirmedOwner, confirmedStale) : confirmedStale) {
177
+ await rm(lockPath, { recursive: true, force: true }).catch(() => { });
178
+ }
179
+ }
180
+ finally {
181
+ await rm(recoveryPath, { recursive: true, force: true }).catch(() => { });
182
+ }
183
+ }
184
+ catch (recoveryError) {
185
+ if (recoveryError.code !== "EEXIST")
186
+ throw recoveryError;
187
+ }
188
+ continue;
189
+ }
190
+ const waitedMs = Date.now() - startedAt;
191
+ if (waitedMs >= timeoutMs) {
192
+ throw new PlanWriterBusyError({
193
+ errorCode: "PLAN_WRITER_BUSY",
194
+ planRoot,
195
+ lockPath,
196
+ waitedMs,
197
+ ...(currentOwner ? { owner: currentOwner } : {}),
198
+ });
199
+ }
200
+ await new Promise((resolveDelay) => setTimeout(resolveDelay, retryMs));
201
+ }
202
+ }
203
+ }
204
+ /**
205
+ * Serialize one logical planner mutation across processes for a planner root.
206
+ * Nested writes in the same async transaction are re-entrant. Reads never take
207
+ * this lock, so secondary processes remain available for inspection.
208
+ */
209
+ export async function withPlanRootWriteLock(root, fn) {
210
+ const planRoot = resolve(root);
211
+ const active = heldRoots.getStore();
212
+ if (active?.has(planRoot))
213
+ return fn();
214
+ const release = await acquirePlanRootLock(planRoot);
215
+ const next = new Set(active ?? []);
216
+ next.add(planRoot);
217
+ try {
218
+ return await heldRoots.run(next, fn);
219
+ }
220
+ finally {
221
+ await release();
222
+ }
223
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-plan/core",
3
- "version": "0.2.26",
3
+ "version": "0.2.28",
4
4
  "private": false,
5
5
  "description": "Harness-agnostic core for Agent Plan: schemas, persistence, ordering, status rollups, and markdown rendering.",
6
6
  "license": "MIT",
package/planner-skill.md CHANGED
@@ -30,9 +30,10 @@ Discover before mutating:
30
30
 
31
31
  1. List features, phases, or tasks using compact list tools.
32
32
  2. Follow the lowest visible ready priority unless the automatic recommendation or an approved deviation says otherwise.
33
- 3. Use `task_recommend` / `planner-task-recommend` when choosing the next task.
33
+ 3. Use `task_recommend` / `planner-task-recommend` when choosing the next task. Treat its `claims` as bounded evidence: priority is a policy signal, a handoff is actionable only when persisted `resumeReady=true`, and Markdown prose or terminal archives never create an action claim.
34
34
  4. Read the exact entity with its full-detail show/get surface when full context is needed.
35
35
  5. Never infer an ambiguous bare reference. Ask for the exact composite reference.
36
+ 6. Never claim that no task is active from counts, feature summaries, or omitted task detail. Use an explicit `activeTaskState`/`activeTasks` result from `plan_get`, `planner-show`, or the lifecycle recommendation. Only `activeTaskState: none` (verified from all persisted task statuses) proves absence; `conflict` means multiple active tasks must be reconciled.
36
37
 
37
38
  Feature and phase statuses are derived from their children. Do not write their status directly; update the relevant child tasks. A `DERIVED_STATUS_READ_ONLY` result is a non-success result.
38
39
 
@@ -45,8 +46,9 @@ When denied:
45
46
  1. Confirm `started` is `false` and read `errorCode` plus `nextActions`.
46
47
  2. Perform only the missing or stale reads listed in `nextActions`. Reads may be completed in any order within the current session.
47
48
  3. If Project Guidelines are listed, call `project_guidelines_show` or `planner-project-guidelines-show` and retain the content while working.
48
- 4. Read each task on every start or resume. Fresh unchanged feature, phase, and linked-requirement reads may be reused across sibling tasks in the same session.
49
- 5. Retry the lifecycle operation. Only `started: true` proves work is active.
49
+ 4. Read each task on every start or resume. Fresh unchanged feature, phase, and linked-requirement reads may be reused across sibling tasks in the same session. When linked requirements are requested, call `requirement_list` or `planner-requirement-list` with the exact `phaseRef` from `nextActions`; a broad unscoped inventory does not attest that every requirement was read. A full feature/phase/task read is complete only when it delivers all canonical Accepted Decision fields (`id`, `title`, `decision`, `rationale`, `implementationNotes`, `acceptedAt`); title-only summaries never satisfy the read gate.
50
+ 5. Use the returned priority-ordered phase work map to review sibling task refs, goals, dependencies, statuses, and remaining capability ownership. Before proposing or creating work, reread the canonical phase and the relevant sibling task full view; never duplicate a capability already owned by another task.
51
+ 6. Retry the lifecycle operation. Only `started: true` proves work is active.
50
52
 
51
53
  Do not convert a denial into a planner status change merely to bypass the gate. Common typed denials include `PROJECT_GUIDELINES_READ_REQUIRED`, `CONTEXT_READ_REQUIRED`, `REQUIREMENTS_READ_REQUIRED`, `START_NOT_ALLOWED`, `ACTIVE_TASK_CONFLICT`, `TASK_DONE`, and persistence verification failures.
52
54
 
@@ -54,7 +56,9 @@ Do not convert a denial into a planner status change merely to bypass the gate.
54
56
 
55
57
  `Project Guidelines` is the canonical project section for coding standards, formatting, styling, verification conventions, and other implementation rules.
56
58
 
57
- - Read it on planner load when present and whenever lifecycle `nextActions` says it is missing or stale.
59
+ `Requirements` are separate declarative product outcomes: user, business, or system capabilities that phases deliver. They have no lifecycle status. Never store coding standards, best practices, formatting rules, verification process, or agent behavior in Requirements; store those only in Project Guidelines. Nested Requirement macro-tasks retain their own implementation status.
60
+
61
+ - Read Project Guidelines on planner load when present and whenever lifecycle `nextActions` says it is missing or stale.
58
62
  - Update it only through `project_guidelines_update`, `planner-project-guidelines-update`, or Pi `/planner project guidelines`.
59
63
  - Explicit planner load automatically and atomically deduplicates legacy `globalRules`, textual `workflowRules`, and project `decisions` into canonical Project Guidelines and Accepted Decisions before recap/context delivery. Ordinary entity reads remain non-mutating. `project_context_migrate` and `planner-project-context-migrate` remain manual preview/recovery diagnostics; repeated applications are idempotent.
60
64
  - The Web UI may display the section for the human supervisor, but guideline-read enforcement applies to agents.
@@ -82,20 +86,30 @@ Every mutation is success-sensitive:
82
86
 
83
87
  ## Handoff protocol
84
88
 
85
- Handoffs are phase-scoped resume documents, not locks. They must be operationally exhaustive, not merely structurally valid.
89
+ Handoffs are phase-scoped resume capsules, not locks. They transfer only the context a cold agent needs to continue safely. Every canonical terminal phase outcome archives its active handoff automatically; terminal phases cannot receive a new handoff.
86
90
 
87
91
  Before writing:
88
92
 
89
93
  1. Resolve one exact phase reference and obtain user confirmation when required.
90
- 2. Run `handoff_prepare` / `planner-handoff-prepare` for that exact phase.
91
- 3. Reconcile every still-relevant detail from an existing handoff; do not append a competing handoff.
92
- 4. Build the versioned `completenessAudit` returned by prepare. Every required category must appear exactly once as `captured` or `not-applicable`, with concrete detail or a substantive reason. Generic values such as `N/A`, `none`, `unknown`, or `see above` are rejected with `HANDOFF_COMPLETENESS_AUDIT_REQUIRED`.
94
+ 2. Run `handoff_prepare` / `planner-handoff-prepare` for that exact phase. Review its bounded, priority-ordered `phaseWorkMap`, then reread the canonical phase and every relevant sibling task full view before describing remaining work; preserve existing capability ownership rather than proposing duplicates.
95
+ 3. **Before generating prose**, read the returned `requiredHumanInputs` and supply them structurally (including `reason`). The returned `draftTemplate` is guidance, not a form: headings are optional and concise free-form resume prose is accepted. Never write `Created at`, `Updated at`, or `Reason` into Markdown: the planner generates those fields before persistence.
96
+ 4. Reconcile every still-relevant detail from an existing handoff; do not append a competing handoff.
97
+ 5. Do not build or copy a completeness audit into the Markdown. Legacy `completenessAudit` input is optional; the planner records derived evidence as structured metadata.
98
+ 6. Do not build or copy a cold-start inventory or five source reviews into the Markdown. Legacy `coldStartInventory`, `sourceReviews`, and `omissionsFound` inputs are optional; the planner derives omitted evidence from persisted state. Include concrete files/symbols, verification, constraints, blockers, and next steps only when they are needed to resume.
99
+
100
+ The resume-critical content is: exact focus and resume point; current/partial state; decisions and constraints; relevant files/symbols; verification and pending checks; blockers; and ordered next actions. Omit categories that have no resume impact.
101
+
102
+ Write the canonical handoff as a compact resume capsule targeting at most 8,000 inline characters (24,000 remains only as an absolute compatibility ceiling). Keep inline only the exact focus, current/partial state, decisions or constraints, relevant verification, blockers, and ordered resume steps. Put extended detail in `.planner/docs/` only when the next agent genuinely needs it; externalization is automatic and does not require a manual inventory.
93
103
 
94
- The mandatory categories are: exact focus and resume point; first resume action; completed work; partial work; remaining work; decisions and rationale; rejected alternatives; files and symbols; branch and worktree; commands and tools; completed verification; pending verification; runtime limitations and workarounds; blockers and risks; user-visible behavior; operator actions; project-specific operating notes; and conversation-only facts.
104
+ Then call `handoff_write` / `planner-handoff-write` once with the preparation token, structured `reason`, compact capsule, optional supporting-document manifest, and reconciled task/phase/feature context. Missing preflight/reason, unresolved placeholders, invalid documents, or failed persistence read-back are typed failures and must never be reported as success.
95
105
 
96
- Keep the canonical handoff within the tool-reported budget (currently 24,000 characters). Essential focus, resume point, first action, risks, and verification status must remain inline. Put extended logs, large mappings, command transcripts, and deep design detail in committed Markdown files under `.planner/docs/`; pass each through `supportingDocuments` with a substantive description of what it contains and why the next agent needs it. Links supplement rather than replace the inline resume contract.
106
+ A successful write persists only a **handoff candidate** and returns `resumeReady: false`. Immediately call `handoff_show` / `planner-handoff-show` with the exact phase reference and read the persisted capsule. If the capsule omits resume-critical context, rewrite it; otherwise call `handoff_verify` / `planner-handoff-verify` with the content hash. Source reviews and omission lists are optional legacy evidence; the planner derives them when omitted. Only a successful verification result with `resumeReady: true` authorizes telling the user that the handoff is resume-ready.
97
107
 
98
- Then call `handoff_write` / `planner-handoff-write` with the preparation token, completeness audit, optional supporting-document manifest, and reconciled task/phase/feature context. Missing categories, oversized bodies, invalid documents, or failed persistence read-back are typed failures and must never be reported as success. Read the persisted handoff back with the exact phase reference and verify its body, content hash, audit metadata, branch, file, command, expected behavior, and next action before stopping. `handoff_list` is a compact paginated summary-only index; use `handoff_show` for one bounded body and its metadata. Clear/archive only after explicit intent or when phase completion makes it obsolete.
108
+ `handoff_list` is a compact paginated index of active handoffs and exposes whether each is resume-ready. Use `handoff_show` for one bounded active body and its metadata; after phase completion/rejection/cancellation, the same exact phase-scoped show call returns the latest terminal archive so its closeout and `.planner/docs/` references remain discoverable. Clear/archive only after explicit intent or when phase completion makes the handoff non-operational.
109
+
110
+ ## Hierarchical description freshness
111
+
112
+ Task description or descriptionRef changes can make the owning phase and feature prose stale; phase description changes can make the owning feature stale. Use `description_freshness` / `planner-description-freshness` to read the deterministic, non-mutating leaf-to-root reconciliation preview. Read the cited child and parent full views, then explicitly update only parent prose that is actually obsolete. Never silently copy child text into a parent or claim the hierarchy is fresh from a generic entity timestamp alone; the preview uses description-specific revisions and returns exact stale parent refs.
99
113
 
100
114
  ## Ideas Inbox and promotion
101
115
 
@@ -162,6 +176,8 @@ Supported interactive command paths:
162
176
  - `/planner handoff write <P00x(F00x)>`
163
177
  - `/planner handoff clear <P00x(F00x)>`
164
178
 
179
+ `handoff_verify` is an agent tool rather than an interactive command; call it only after `handoff_show` completes the separate persisted read-back.
180
+
165
181
  Pause, switch, deviation, recommendation, requirement, and decision operations are available through the registered Pi tools below rather than every interactive `/planner` path.
166
182
 
167
183
  ### Dashboard, export, and guard
@@ -178,25 +194,25 @@ Pause, switch, deviation, recommendation, requirement, and decision operations a
178
194
 
179
195
  The MCP adapter publishes these tools:
180
196
 
181
- - Core: `planner-version`, `planner-init`, `planner-show`, `planner-repair`, `planner-cleanup-orphan-phases`, `planner-export`, `planner-authorize-bypass`, `planner-clear-bypass`, `planner-load`, `planner-disable`, `planner-web`.
197
+ - Core: `planner-version`, `planner-init`, `planner-show`, `planner-description-freshness`, `planner-repair`, `planner-cleanup-orphan-phases`, `planner-export`, `planner-authorize-bypass`, `planner-clear-bypass`, `planner-load`, `planner-disable`, `planner-web`.
182
198
  - Ideas: `planner-idea-list`, `planner-idea-show`, `planner-idea-create`, `planner-idea-update`, `planner-idea-delete`, `planner-idea-promotion-begin`, `planner-idea-promotion-finalize`.
183
- - Project: `planner-project-language`, `planner-project-discuss`, `planner-project-guidelines-show`, `planner-project-guidelines-update`, `planner-project-context-migrate`, `planner-requirement-list`, `planner-requirement-create`, `planner-requirement-update`, `planner-requirement-delete`.
199
+ - Project: `planner-project-language`, `planner-project-discuss`, `planner-project-guidelines-show`, `planner-project-guidelines-update`, `planner-project-context-migrate`, `planner-accepted-decision-create`, `planner-accepted-decision-update`, `planner-accepted-decision-delete`, `planner-requirement-list`, `planner-requirement-create`, `planner-requirement-update`, `planner-requirement-delete`.
184
200
  - Features: `planner-feature-list`, `planner-feature-add`, `planner-feature-show`, `planner-feature-discuss`, `planner-feature-update`, `planner-feature-delete`.
185
201
  - Phases: `planner-phase-list`, `planner-phase-add`, `planner-phase-show`, `planner-phase-discuss`, `planner-phase-update`, `planner-phase-delete`.
186
- - Tasks: `planner-task-list`, `planner-task-add`, `planner-task-show`, `planner-task-discuss`, `planner-task-update`, `planner-task-delete`, `planner-task-recommend`, `planner-task-deviation`, `planner-task-pause`, `planner-task-switch`, `planner-task-start`, `planner-task-complete`, `planner-task-checklist-toggle`, `planner-task-checklist-add`, `planner-task-checklist-remove`.
187
- - Handoffs: `planner-handoff-list`, `planner-handoff-show`, `planner-handoff-prepare`, `planner-handoff-write`, `planner-handoff-clear`.
202
+ - Tasks: `planner-task-list`, `planner-task-add`, `planner-task-show`, `planner-task-discuss`, `planner-task-update`, `planner-task-dependency-add`, `planner-task-dependency-delete`, `planner-task-delete`, `planner-task-recommend`, `planner-task-deviation`, `planner-task-pause`, `planner-task-switch`, `planner-task-start`, `planner-task-reopen`, `planner-task-complete`, `planner-task-checklist-toggle`, `planner-task-checklist-add`, `planner-task-checklist-remove`.
203
+ - Handoffs: `planner-handoff-list`, `planner-handoff-show`, `planner-handoff-prepare`, `planner-handoff-write`, `planner-handoff-verify`, `planner-handoff-clear`.
188
204
 
189
205
  ## Pi tool inventory
190
206
 
191
207
  The Pi adapter registers these tools:
192
208
 
193
209
  - Ideas: `idea_list`, `idea_show`, `idea_create`, `idea_update`, `idea_delete`, `idea_promotion_begin`, `idea_promotion_finalize`.
194
- - Project and requirements: `project_set_language_preferences`, `project_update`, `project_guidelines_show`, `project_guidelines_update`, `project_context_migrate`, `requirement_list`, `requirement_create`, `requirement_update`, `requirement_delete`.
195
- - Plan: `plan_init`, `plan_get`, `plan_render`, `plan_repair`, `plan_cleanup_orphan_phases`, `plan_authorize_bypass`, `plan_clear_bypass`.
210
+ - Project and requirements: `project_set_language_preferences`, `project_update`, `project_guidelines_show`, `project_guidelines_update`, `project_context_migrate`, `accepted_decision_create`, `accepted_decision_update`, `accepted_decision_delete`, `requirement_list`, `requirement_create`, `requirement_update`, `requirement_delete`.
211
+ - Plan: `plan_init`, `plan_get`, `description_freshness`, `plan_render`, `plan_repair`, `plan_cleanup_orphan_phases`, `plan_authorize_bypass`, `plan_clear_bypass`.
196
212
  - Features: `feature_list`, `feature_get`, `feature_create`, `feature_discuss`, `feature_update`, `feature_delete`.
197
- - Phases and decisions: `phase_list`, `phase_get`, `phase_create`, `phase_update`, `phase_delete`, `decision_record`.
198
- - Tasks: `task_list`, `task_get`, `task_create`, `task_update`, `task_delete`, `task_recommend`, `task_deviation`, `task_pause`, `task_switch`, `task_start`, `task_complete`, `task_checklist_toggle`, `task_checklist_add`, `task_checklist_remove`.
199
- - Handoffs: `handoff_list`, `handoff_show`, `handoff_prepare`, `handoff_write`, `handoff_clear`.
213
+ - Phases and decisions: `phase_list`, `phase_get`, `phase_create`, `phase_discuss`, `phase_update`, `phase_delete`, `decision_record`.
214
+ - Tasks: `task_list`, `task_get`, `task_create`, `task_update`, `task_dependency_add`, `task_dependency_delete`, `task_delete`, `task_recommend`, `task_deviation`, `task_pause`, `task_switch`, `task_start`, `task_reopen`, `task_complete`, `task_checklist_toggle`, `task_checklist_add`, `task_checklist_remove`.
215
+ - Handoffs: `handoff_list`, `handoff_show`, `handoff_prepare`, `handoff_write`, `handoff_verify`, `handoff_clear`.
200
216
  - Dashboard and lifecycle: `planner-web`, `planner-load`, `planner-stop`.
201
217
  - Deprecated compatibility aliases: `plan_get_handoff`, `plan_write_handoff`, `plan_delete_handoff`. Prefer the entity-scoped handoff tools.
202
218