Repository navigation
Nudge Claude to declare a plan document when it reads one - #1313
Conversation
A separate synchronous PostToolUse entry on Read: the catch-all is async, and an async hook's context never reaches the agent before its next step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
PR Summary by QodoNudge Claude to declare plan documents after reading them
AI Description
Diagram
High-Level Assessment
Files changed (9)
|
Code Review by Qodo
1. Published hooks never send plan reads
|
| var request = new JsonObject { ["session_id"] = SessionId, ["path"] = Path }; | ||
| if (Content is not null) request["content"] = Content; |
There was a problem hiding this comment.
1. Published hooks never send plan reads 🐞 Bug ≡ Correctness
ClaudePlanRead.ToRequest assigns strings directly to a JsonObject, which requires unavailable JSON metadata in the CLI's NativeAOT build. On a qualifying Markdown read, that assignment throws before the POST, and HandlePlanReadCore catches the exception and exits without a reminder; RenderHookOutput contains the same failure pattern if request construction is fixed alone.
Agent Prompt
## Issue description
Direct string assignments to the new JSON objects fail under the CLI's NativeAOT configuration, so the plan-read handler silently exits before posting or rendering a nudge.
## Fix Focus Areas
- src/Capacitor.Cli/Harness/Claude/ClaudePlanRead.cs[28-31]
- src/Capacitor.Cli/Harness/Claude/ClaudePlanRead.cs[43-49]
- src/Capacitor.Cli/Commands/Harness/ClaudeHookCommand.cs[1335-1339]
## Recommended Fix
Construct every string-valued JSON node through the existing parsed-string approach, including request fields and hook output fields. Verify both the outbound request and emitted context with a published NativeAOT binary.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
Not reproduced: the published NativeAOT binary, run against a stub server, sends the request and writes the hook output; the existing hook code assigns strings into JsonObject the same way. dotnet publish reports no IL warnings.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9ec347f365
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| using var content = new StringContent(read.ToRequest().ToJsonString(), Encoding.UTF8, "application/json"); | ||
| using var resp = await client.PostOnceAsync($"{Url}/hooks/plan-read", content, clock.Time, budget.Remaining); |
There was a problem hiding this comment.
Redact Markdown content before posting it
When a Markdown file contains a token, password, or other credential-like value, read.ToRequest() serializes the raw tool_response.file.content and this new request sends it directly to /hooks/plan-read. This bypasses the SecretRedactor used by TranscriptCapture.Encode, so secrets removed from normal transcript uploads are transmitted verbatim and may be logged or stored by the endpoint; redact the content before constructing this request.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed: the content is passed through SecretRedactor before it is cut, with a margin so a secret straddling the cut is still matched whole. Verified with the published AOT binary against a stub server: the request carries key [REDACTED].
HookBudget holds back a 1.5 s reserve, so a 2 s ceiling left nothing after startup and the exclusion probe; the ceiling now matches the entry's 3 s timeout in hooks.json. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closes #1312 — AI-3472
What & why
An agent handed a plan file often never declares it through kcap-plans. A new synchronous
PostToolUsehook onRead(kcap hook --claude --plan-read) sends a.mdread to kcap-server'sPOST /hooks/plan-read(kurrent-io/kcap-server#2240) and hands the reminder it answers with to the agent as hook context, at most once per session and document. Same gate and opt-out as the SessionStart plans nudge; only the first 16,384 characters of the file are sent, after secret redaction.Where to look
The catch-all
PostToolUsestays async; this is a second, Read-only entry inhooks.json, because an async hook's context does not reach the agent before its next step. It dispatches ahead of the spool drain inProgram.csand fails open on a 2 s budget. Needs kcap-server#2240 deployed; until then the endpoint 404s and the hook stays silent.Verification
.mdRead with the server round trip in ~270 ms; both exit 0.{}, skipped for non-Markdown, unregistered kcap-plans and the opt-out; parser and ledger.ClaudeHookCommandTests94/94.dotnet publish -c Release: 0 IL warnings.🤖 Generated with Claude Code