Skip to content

block-hook-bypass: echo the concrete Write call for the blocked target (option B from #4261) #5668

Closed

Description

@kyle-sexton

Scope (owner decision 2026-10-12, recorded 2026-10-13): measure which tool follows each denial first, then choose between accept and briefing. This issue builds the measurement field only. It does not build option B or change the denial message. The run and the choice are split to #8986.

Follow-up to #4261, filed under its owner decision: file option B only if block-hook-bypass rejections are still followed by failed retries.

Evidence

Post-release transcript join (records after 2026-09-30T02:17Z, 129 sessions, this WSL machine's local transcripts only). Attribution is by message string, not guard name.

  • block-hook-bypass rejections: 2039 by message string (2041 in the retry scan).
  • 1422 of 2041 were followed by a successful Write/Edit with no failure in the next 2 tool calls (one correction).
  • 86 of 2041 (4.2%) had another failed or blocked call in the next 2 tool calls; 55 kept retrying; 23 immediately repeated the same bypass block.
  • Overall blocked/denied tool_result records: 2466, median 0 per session, mean 19.12, max 1227; excluding the 3 largest sessions mean 0.54, max 39 (baseline 17).

Option B

The block-hook-bypass message echoes the concrete Write call for the blocked target instead of only naming the Write/Edit remedy. This grows the message #4679 shortened, and the guard parses shell strings heuristically, so reconstructing the target path and content may be unreliable.

Decision needed

The owner chose option A on #4261 (keep the existing remedy). Whether a 4.2% failed-retry tail justifies option B is the owner's call; nothing is implemented.

The owner's later decision (2026-10-12) is to measure first; see the scope line above and #8986.

Split to #8986: run /session-flow:audit-friction on both machines over post-2026-10-04 denials, the owner's accept-or-briefing choice, and the re-measure after any change.

Done when (this issue: the next-tool field)

  • plugins/session-flow/skills/audit-sessions/scripts/friction_scan.py adds next_tool (and whether that call succeeded) to each hook-block denial event, derived from the next tool_use/tool_result in the same transcript file (the scanner already walks records in order: add, _assistant, _tool_result, :190-274). Check: the PR's CI runs plugins/session-flow/skills/audit-sessions/scripts/tests/test_friction_scan.py (pytest) and it passes.
  • plugins/session-flow/skills/audit-friction/scripts/friction.py mine aggregates next_tool per event key and side, in both JSON and md output. Check: the PR's CI runs plugins/session-flow/skills/audit-friction/scripts/tests/test_friction.py and it passes.
  • test_friction_scan.py covers three cases: next call is Write/Edit, next call repeats the shell form, and no next call. Check: as above.
  • A .changes/session-flow/ fragment exists. Check: scripts/new-changelog-fragment.sh, and the PR's CI runs scripts/check-changelog-fragments.sh --check and it passes.

Activity

  1. added
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Oct 1, 2026
  2. kyle-sexton commented on Oct 3, 2026

    @kyle-sexton
    MemberAuthor

    A second machine's transcripts (Windows 11, Git Bash, 764 sessions plus 4,041 subagent transcripts, 2026-08-28 to 2026-10-03, Claude Code 2.1.241 to 2.1.288) add data for this decision. Counts come from a finder pass re-derived by an independent verifier; where they differed, the verifier's number is used.

    Volume. block-hook-bypass is the first BLOCKED: reason on 1,620 of 2,948 denials (55%). By ISO week: W38 433, W39 578, W40 353 in about 5.2 days, so the volume has not fallen since the #4679 message cut (merged as #4711).

    Rate per caller. 1,265 of the 1,620 came from subagents, which also made 76.3% of shell calls. Per 1,000 shell calls: main 10.33, subagents 11.42. Subagents are not worse per call; they make most of the calls.

    Retries. Of the denials not user-rejected, cancelled or interrupted, 314 of 1,654 block-hook-bypass denials (19%) were followed within two assistant messages by a same-tool call at least 60% similar, 22 of them repeated loops. So about 81% change approach after one denial, consistent with this issue's WSL figure (1,422 of 2,041 followed by a successful Write/Edit).

    The remedy line is already there. All 1,643 block-hook-bypass denials on disk carried "Use the Write or Edit tool instead of a shell file-write workaround." On current origin/main it is plugins/guardrails/hooks/block-hook-bypass.sh:1376, present since the guard's first commit (303d0ba, #118) and kept by #4711.

    What this suggests for the decision (inferred, not verified). The denial text is not the gap; option B would grow a message the model already acts on. The steady rate of about 10 attempts per 1,000 shell calls in both main and subagent contexts points at how agents are briefed. A third option beside A and B: where plugins brief workers (subagent dispatch templates), state that files are written with the Write or Edit tool, then re-measure the per-1,000 rate. #5993 step 1 (measure which denials lanes hit, by source) overlaps and could carry the measurement: for each block-hook-bypass denial, which tool the next call in the same transcript used.

    Options as the producing session framed them, for the owner: (1) accept the volume as the guard working as designed; (2) address briefing first, as above; (3) recommended by that session: measure the next tool after each denial first, then choose between 1 and 2.

  3. kyle-sexton commented on Oct 3, 2026

    @kyle-sexton
    MemberAuthor

    Corrects the per-caller rate in the second-machine comment above.

    "Subagents are not worse per call" is wrong. The verifier's counts give 355 block-hook-bypass denials in 34,357 main-context shell calls (10.33 per 1,000) and 1,265 in 110,781 subagent shell calls (11.42 per 1,000), so subagents hit the guard about 10.5% more often per shell call. Subagents still account for most of the volume because they make 76.3% of shell calls, but the rate is not equal. The later sentence "about 10 attempts per 1,000 shell calls in both main and subagent contexts" should read: about 10 per 1,000 in main contexts and about 11 per 1,000 in subagents. The inferred point that briefing, not the denial text, drives the volume is unchanged; if the briefing option is tried, re-measure both rates separately.

    Anchor, for the code citation. On origin/main the remedy line is echoed inside block_bypass(), which starts at plugins/guardrails/hooks/block-hook-bypass.sh:1372; the line itself is at :1376 as already cited, and is skipped only for the powershell-computed-positional form (:1375).

    Source: local handoff item 20261003-101457-corrections-to-6073-and-6074-6080 (retired into these comments).

  4. kyle-sexton commented on Oct 4, 2026

    @kyle-sexton
    MemberAuthor

    This was generated by AI during triage.

    Agent Brief

    Type: Feature
    Summary: The owner decides how to respond to the block-hook-bypass failed-retry tail: echo the concrete Write call in the denial (option B), fix how agents are briefed, or accept the current volume.

    Current behavior:
    Every block-hook-bypass denial already tells the model to use the Write or Edit tool instead of a shell file-write workaround. Measurements on two machines show about 81% of denials change approach after one denial, and 4.2% (one machine) to 19% (the other) are followed by a similar retry. There are about 10 denials per 1,000 main-context shell calls and about 11 per 1,000 subagent shell calls. Volume has not fallen since the message was shortened.

    Desired behavior:
    Depends on the decision:

    1. Accept the volume as the guard working as designed. Nothing changes.
    2. Fix briefing: where plugins brief workers, state that files are written with the Write or Edit tool, then re-measure the main-context and subagent rates separately.
    3. Option B: the denial echoes a concrete Write call for the blocked target. This grows the message, and the target is reconstructed from shell text heuristically.

    Acceptance criteria:

    • The owner's choice is recorded on this issue
    • For option 2 or 3: the per-1,000 denial rate and the similar-retry rate are re-measured after the change, main context and subagents separately

    Out of scope:

    • Changing what block-hook-bypass blocks

    Routing: human-gated (needs decision)

    The owner chose option A on #4261, and this issue asks whether the new evidence changes that ruling. That is a product call. Recommended: measure first. For each denial, record which tool the next call used, then choose between options 1 and 2. Option B has the weakest evidence: the denial text already names the remedy, and most agents act on it.

    Overlap: #5993 (already triaged, work-class: read-only) has step 1, "measure which denials lanes hit, by source", which could carry that measurement. It is not the same decision, so I did not cluster it.

    Basis: verified. The remedy line is echoed in block_bypass() at plugins/guardrails/hooks/block-hook-bypass.sh:1376, per the second-machine comment and its correction. #5993's labels were read this session.

  5. added
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    and removed
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Oct 4, 2026
  6. kyle-sexton commented on Oct 13, 2026

    @kyle-sexton
    MemberAuthor

    Decision recorded. The owner approved applying independently verified recommendations on 2026-10-12.

    Asks whether the block-hook-bypass denial should echo a concrete Write call (option B) given a failed-retry tail after denials. Two machines show about 81% of agents change approach after one denial and every denial already names the Write/Edit remedy, so the open choice is accept, fix worker briefing, option B, or measure first.

    Verification notes:

    Verified by an independent fresh-context check (confirmed, risk low): gh issue view 5668 (3 comments; owner option A on #4261 is cited, no new owner decision); origin/main plugins/guardrails/hooks/block-hook-bypass.sh:1364-1369 (block_bypass, remedy echo at :1368); git log block-hook-bypass.sh (#6303, #7261, #5997); gh pr view 6303 (body: message cut, Closes #4679), 6710 (merged), 6708 (CLOSED 2026-10-10, owner option A keeps main's guard); gh issue view 5993 (OPEN, one triage comment, step 1 read-only); gh pr view 6692 body (audit-friction, Related #5993)

    Admission to an agent lane stays with the work-class rules and owner ratification.

  7. added
    status: confirmedA maintainer confirmed the item is real and in scope; ready to be worked.
    status: readyTriaged, unblocked, and fully specified; eligible to pick up.
    agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.
    and removed
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    status: confirmedA maintainer confirmed the item is real and in scope; ready to be worked.
    on Oct 22, 2026
  8. added
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    on Oct 22, 2026
  9. melodic-automation-lanes commented on Oct 22, 2026

    @melodic-automation-lanes
    Contributor

    The lanes App's pull request #9174 links this issue with Refs:, not Closes, so the merge lane never merges it. report-outcome added needs-human so a person merges or closes it. When it merges, work-unblock removes this needs-human and agent-ready and adds needs-triage, so triage decides what work is left. To keep the issue held after the merge, add needs-owner-decision.

  10. melodic-automation-lanes commented on Oct 23, 2026

    @melodic-automation-lanes
    Contributor

    The lanes App's pull request #9174 merged with Refs:, so this issue stays open and nothing starts the lane on it again. work-unblock added needs-triage and removed agent-ready, so triage decides what work is left.

  11. added
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    and removed
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.
    on Oct 23, 2026
  12. kyle-sexton commented on Oct 23, 2026

    @kyle-sexton
    MemberAuthor

    Done in #9174: friction_scan.py:242,298 records next_tool/next_ok on each hook-block denial; friction.py mine tallies them per side and key in JSON and markdown (friction.py:173-193,234-240); tests cover Write-next, repeated shell form and no next call (test_friction_scan.py:229-245); the changelog entry shipped (plugins/session-flow/CHANGELOG.md:13). The measurement run, the accept-or-briefing choice and the re-measure continue in #8986.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageNot yet classified. Floor until a type and one priority tier are set.status: readyTriaged, unblocked, and fully specified; eligible to pick up.

    Fields

    No fields configured for Feature.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions