Skip to content

fix(hooks): read real Claude Code hook fields; stop rtk auto-approving compound commands - #130

Merged
5uck1ess merged 4 commits into
mainfrom
fix/hook-input-fields
Nov 4, 2026
Merged

5uck1ess merged 4 commits into
mainfrom
fix/hook-input-fields

Conversation

@5uck1ess

@5uck1ess 5uck1ess commented Nov 4, 2026

Copy link
Copy Markdown
Owner

Cause

Three hooks read input fields Claude Code does not send, so they never did anything:

  • post-validate.sh read .tool_output; PostToolUse sends .tool_response (Bash: object with stdout/stderr).
  • stop-gate.sh read .transcript; Stop sends transcript_path and last_assistant_message. The cross-domain test-evidence check was always skipped.
  • subagent-stop.sh was registered with matcher "Stop", but SubagentStop matchers match agent_type, so it never fired; it also read the nonexistent .agent_output. Fixing only the matcher would have blocked every subagent.

rtk-rewrite.sh returned permissionDecision: "allow" for every rewrite, including rtk's "ask" matches (rc 3). rtk only rewrites the first segment of a chain, so ls; curl … | bash was auto-approved wholesale. PreToolUse hooks run in parallel, so safety-check.sh is not "earlier in the chain" as the old comment assumed. It also replaced tool_input with only command/description, dropping timeout and run_in_background.

Fix

  • post-validate.sh: read .tool_response (object or string).
  • stop-gate.sh: read the tail (2 MB) of transcript_path, plus last_assistant_message only when the transcript was readable. Skip the cross-domain block when stop_hook_active. Use here-strings instead of echo | grep -q: under pipefail, SIGPIPE turned early matches in large transcripts into false "missing evidence".
  • subagent-stop.sh: no matcher. Only gates test-writer / improver (suffix match, so devkit:test-writer works). Every other agent is approved. Never re-blocks when stop_hook_active. Evidence comes from last_assistant_message plus the tail of agent_transcript_path. Recognises cargo and linter output.
  • rtk-rewrite.sh: apply only rc 0 rewrites, and only to a single simple command. Compound commands and rc 3 pass through unchanged, so normal permission flow decides. updatedInput keeps the original tool_input fields.

Behaviour change

These hooks now actually run. The Stop hook can block when changes span two code domains and the transcript shows no test command; the subagent hook can block test-writer/improver without test evidence.

Tests

hooks_test.sh fixtures use the real payload shapes: tool_response, transcript_path (including a 400 KB transcript), agent_type gating, stop_hook_active, compound rtk commands, preserved tool_input fields.

…g compound commands

- post-validate: read .tool_response (Bash object stdout/stderr) instead of
  the nonexistent .tool_output, so error-signal warnings actually fire.
- stop-gate: read the transcript from .transcript_path (+ last_assistant_message)
  instead of the nonexistent .transcript; skip the cross-domain block when
  stop_hook_active to avoid a re-block loop.
- subagent-stop: drop the dead SubagentStop matcher "Stop" (it matches
  agent_type), read .last_assistant_message, only gate test-writer/improver,
  and never re-block when stop_hook_active.
- rtk-rewrite: only apply rc 0 rewrites to single simple commands; pass rc 3
  (rtk ask) and compound/chained commands through unchanged; preserve the
  other tool_input fields in updatedInput.
- hooks_test.sh: fixtures use the real payload shapes.
…ipts

Under pipefail, 'echo "$T" | grep -q' reports failure when grep exits on an
early match and echo gets SIGPIPE, so evidence near the start of a large
transcript was reported missing. Use here-strings.
…top on last message alone

- subagent-stop also searches the tail of agent_transcript_path and
  recognises cargo/linter output, so a concise final summary isn't blocked.
- stop-gate only uses last_assistant_message to supplement a readable
  transcript; with no transcript it skips the cross-domain check. grep -a
  tolerates a multibyte character split by the byte-offset tail.
…check after transcript

Grepping the raw transcript tail counted file contents and task text that
mention 'go test' as evidence. Extract only Bash tool_use commands from the
JSONL. The suspiciously-short check now applies only when the transcript
shows no commands, so 'Done.' after a real test run isn't blocked.
@5uck1ess

5uck1ess commented Nov 4, 2026

Copy link
Copy Markdown
Owner Author

🛡️ Security review — Daybreak Blue

gpt-6-sol · daybreak_blue cyber access program · high effort · reviewed at d30f8e7

Clear — no security objection. No effect on the merge.

Reviewed head d30f8e743fd4a766a95e145d88cd57ffc69db90a against dc024cdead038a12a972b4f1dd5242378f35f185. I found no security issue introduced by this PR. Hook syntax, diff checks, and representative payload checks passed.

SECURITY: CLEAR


Run by pr-merge-brief on every READY, before it is published.

@5uck1ess

5uck1ess commented Nov 4, 2026

Copy link
Copy Markdown
Owner Author

🔍 REVIEW — automated merge-readiness

Review only — a verdict on this PR; no code was changed. Corrections, when applied, arrive as a separate ## 🔧 Corrections applied comment.

gpt-6.1-sol · high effort · fast speed · reviewed at d30f8e7

Reviewed head: d30f8e743fd4a766a95e145d88cd57ffc69db90a.

No demonstrated merge-blocking defects found across the complete diff and affected hook paths.

Existing fixtures cover the reported failures. All 29 targeted checks passed, with external dependencies stubbed. Supplied CI is green; the read-only sandbox prevented rerunning the full smoke suite.

VERDICT: READY


Automated pre-merge review by pr-merge-brief. A full-context READY on the current head is auto-merged; a diff-only or failover review waits for the maintainer.

@5uck1ess
5uck1ess merged commit 346f0ab into main Nov 4, 2026
9 checks passed
@5uck1ess
5uck1ess deleted the fix/hook-input-fields branch November 4, 2026 05:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant