ADR 0069: Make native-tool path mistakes recoverable
- Status: Accepted for implementation
- Date: 2026-08-10
- Deciders: PI-Desktop core
- Amends: D185, D192
- Related: 02-agent-runtime · 03-tools-and-permissions · 08-component-spec §9 · E2E-019e · E2E-083 · E2E-095
Context
Recent durable sessions showed a repeated path-shape mismatch between the model and the native tools. Read was called with a directory after a guessed file name did not exist, and Grep.path was repeatedly given one explicit file even though the host accepted only directories. These were recoverable inspection mistakes, but they were reported as generic execution failures.
The transcript compounded the problem. A failed tool row correctly retained its own error, while the containing activity group also derived a terminal failure from any child error. A later successful search and final answer could therefore leave a completed turn labeled Failed after ....
Decision
- Preserve D185's Agent core and deferred-tool boundary.
GlobandGrepremain on demand; each new Agent prompt must activateGlobthroughToolSearchwhen a directory must be listed or a file name is uncertain. - Enforce and advertise one portable path contract:
Read.pathaccepts an existing regular text file, never a directory.Glob.pathaccepts a directory.Grep.pathaccepts either one file or a directory tree. An explicit file is searched directly, andincludestill filters its basename.
- A directory passed to
Readreturns publicINVALID_ARGUMENT, notTOOL_FAILED. Its result includessuggestedTool: "Glob"and boundedsuggestedArgswith the original path andpattern: "**/*"so the model can correct the call without guessing. - Host definitions, runtime TypeBox schemas, main-Agent guidance, and subagent guidance carry the same path semantics. Subagents do not receive
ToolSearch, so their guidance refers only to tools actually available to the definition. - A tool error remains visible and auto-expanded on its own ToolCallRow. The activity group represents processing duration and step containment only; it never infers terminal turn failure from child rows. Terminal agent errors, the TurnOutcomeCard, sidebar state, and notifications remain the single turn-outcome surfaces.
No host protocol or storage-schema version changes. The existing tool result is already a structured JSON value, and the Grep input shape remains a string.
Consequences
- Common single-file Grep calls stop failing on a directory-only contract.
- A mistaken directory Read receives an actionable, non-retriable correction rather than an ambiguous execution failure.
- Recovered turns no longer look terminally failed, while the original failed call remains auditable in the expanded transcript.
- Deferred search keeps the measured first-request context reduction from D185; the prompt must make the per-user-prompt activation boundary explicit.
Alternatives considered
Put Glob and Grep back in the Agent core
Rejected. It reverses D185's bounded first-request context decision, and the observed failure can be corrected without paying both schemas on every prompt.
Silently execute Glob when Read receives a directory
Rejected. It would make the audited tool name disagree with the executed operation and return a result shape outside Read's contract.
Keep Grep directory-only
Rejected. A direct file path is unambiguous, cheaper than walking its parent, and common across coding-agent tool conventions.
Mark the activity group failed after the turn settles
Rejected. Settlement does not mean every intermediate tool call succeeded; the dedicated terminal outcome surfaces already own turn failure.