backnotprop / backnotprop/plannotator
Claude Code: plan annotations are silently dropped when the plan is approved (approve path never sends feedback to the agent)
- Dominant language
- TypeScript
- Stars
- 8.7k
- Forks
- 649
- Avg merge
- 11h 12m
- Merged PRs (30d)
- 109
Description
## Summary
When reviewing a plan in the annotation UI for Claude Code, any annotations added before clicking **Approve** are silently dropped — the agent never sees them. Annotations only reach the agent when the plan is **denied**. The Gemini CLI approve path already forwards feedback via `systemMessage`; the Claude Code approve path does not.
## Environment
- plannotator 0.27.4 (also present in the 0.27.3 source I checked)
- Claude Code on WSL2, PermissionRequest hook on ExitPlanMode
## Steps to reproduce
1. Enter plan mode in Claude Code, have the agent submit a plan (ExitPlanMode).
2. The plannotator review page opens. Add one or more line annotations.
3. Click **Approve** (with annotations still present).
4. The plan is approved and execution continues.
## Actual behavior
The agent receives only the allow decision. None of the annotations are delivered into the conversation — the agent never learns the feedback existed. The annotations are written to `/.annotations.md`, but nothing surfaces them to the agent.
## Expected behavior
On approve, forward the collected feedback to the agent (e.g. as `systemMessage`), mirroring the Gemini CLI path.
## Root cause
`server/index.ts` (0.27.4):
- **Gemini approve path (line 2249)** — feedback IS forwarded:
```ts
console.log(result.feedback ? JSON.stringify({ systemMessage: result.feedback }) : "{}");
```
- **Claude Code approve path (lines 2263–2292)** — outputs `hookSpecificOutput` with `decision.behavior: "allow"` (+ `updatedInput` / `updatedPermissions`) but never includes `result.feedback`. Only the deny branch of the Claude Code path includes feedback (as `decision.message`).
## Suggested fix
In the Claude Code approve branch, attach the feedback, e.g.:
```ts
console.log(
JSON.stringify({
hookSpecificOutput: {
hookEventName: "PermissionRequest",
systemMessage: result.feedback || undefined,
decision: { /* existing allow output */ },
},
})
);
```
The current UX makes "annotate + approve" (notes for the agent to keep in mind while executing) indistinguishable from "plain approve", which reads as data loss rather than intent.
## Workaround
Deny the plan to have feedback delivered, or manually paste the contents of `.annotations.md` into the conversation.
Contributor guide
Assessment
This issue has not been assessed yet.