Repository navigation
Pass CHOCK_CAN_ASK to guards from the vendored dispatcher (C1b) - #255
Merged
Merged
Conversation
A guard cannot tell whether its ask (exit 3) becomes a prompt or a block, because no client identity reaches it. The C1 guard needs that to let a credential switch through on clients that cannot prompt. - guard_runner: CAN_ASK_ENV = "CHOCK_CAN_ASK". run_guard_detailed() and evaluate() take keyword-only can_ask: bool | None = None; when not None the guard's env gets "1" or "0". interpreter_env() drops any inherited value, so it is unset outside the dispatcher (eval replay, direct calls). - dispatch.py.tmpl: _judge() passes can_ask, resolved at render time by runtime_bundle.pre_tool_can_ask(): honours_escalate of the vendor's native pre_tool gate; where several events map to pre_tool (cursor, devin), the one wire_events names. Render time, not VENDOR at runtime, because the vscode_copilot bundle has no VENDOR dict. - Values: 1 claude_code, vscode_copilot, gemini_cli; 0 cursor, codex_cli, devin, grok, tabnine, windsurf, antigravity. - tests/test_guard_can_ask.py: per-vendor value through each rendered bundle, unset in eval replay and direct calls, verdicts unchanged for ask/block/clean guards. test_pretooluse_logging stubs accept **kwargs. - Runtime goldens and .chock/bin regenerated (chock sync --check clean). Opt-in: no guard reads the variable yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
Review answer: Cursor wires command guards at beforeShellExecution, which
honours ask, so a per-agent constant of 0 was wrong there.
- runtime_bundle.pre_tool_can_ask(agent) now returns {wire event:
honours_escalate} for every event the agent maps to pre_tool, rendered
as a literal (_CHOCK_CAN_ASK) so the vscode_copilot bundle still needs
no runtime VENDOR.
- dispatch.py.tmpl: _chock_can_ask(raw) looks the payload's own event
name up in that map: the vendor's event_key plus the shared keys, or
cursor_wire(raw) in the cursor bundle (same inference its parse and
respond use). An unknown or unnamed event is False.
- Cursor: beforeShellExecution and an unnamed shell payload -> 1,
preToolUse -> 0. Other vendors unchanged.
- spec/script-backed-gates.md documents CHOCK_CAN_ASK: values, when it
is unset, and that a guard treats unset as "can ask".
- Goldens and .chock/bin regenerated (chock sync --check clean).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
jothimani-rajendran
marked this pull request as ready for review
October 11, 2026 15:13
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Plan item C1b (org-plan
plan/one-oh-gaps.mdat a8d101f, plus the orchestrator's review answer). It blocks C1 (chock-catalog#254). A guard can't tell whether its ask (exit 3) will show as a prompt or turn into a block, because the guard never learns which client is running it. This PR has the vendored dispatcher tell it, through theCHOCK_CAN_ASKenvironment variable.guard_runner: addsCAN_ASK_ENV = "CHOCK_CAN_ASK".run_guard_detailed()andevaluate()take a keyword-onlycan_ask: bool | None = None. When it isn't None, the guard's environment gets"1"or"0".interpreter_env()drops any inherited value, so the variable is unset outside the dispatcher (eval replay, direct calls). The caller's environment can never set it.runtime_bundle.pre_tool_can_ask(agent)builds a map from each wire event the agent maps topre_toolto itshonours_escalatevalue. The map is rendered into the dispatcher as a literal (_CHOCK_CAN_ASK), because the vscode_copilot bundle has noVENDORdict to read at runtime.dispatch.py.tmpl,_judge()passescan_ask=_chock_can_ask(event.raw). That function looks up the payload's own event name in the map. The name comes from the vendor'sevent_keyplus the shared keys. The cursor bundle usescursor_wire(raw), the same inference its parse and respond use. An event the runtime can't name, or one missing from the map, gives False.spec/script-backed-gates.mddocumentsCHOCK_CAN_ASK: its values, when it is unset, and that a guard must treat unset as "can ask".PreToolUse, vscode_copilotPreToolUse, gemini_cliBeforeTool, cursorbeforeShellExecution/beforeMCPExecution(also an unnamed Cursor shell payload)preToolUse,beforeReadFile,beforeTabFileRead; codex_cli, devin, grok, tabnine, windsurf pre-tool; vscode_copilotpreToolUse(vendor data has no value); any unknown or unnamed eventMeasured (the rendered cursor bundle, a
beforeShellExecutionpayload, a guard that printsconfirm firstand exits 3):So Cursor does show the ask at
beforeShellExecution, andCHOCK_CAN_ASK=1there is correct.Definition of done
chock check→ All checks passed (locally)chock sync --repo . --checkcleanpytest -qgreen locally (3504 passed, 83 skipped, Python 3.13);tests/test_guard_can_ask.pyhas 30 tests: per-vendor value through each rendered bundle; CursorbeforeShellExecution1,preToolUse0, unnamed shell payload 1; an unknown event 0; unset in eval replay and direct calls; ask, block and clean verdicts unchangedchangelog.d/guard-can-ask-env.mdruff check .andruff format --check .cleanClaims
can_askset to True, False or None.🤖 Generated with Claude Code