Routing guard refusal quotes the words it matched - #489
Open
EdbertChan wants to merge 5 commits into
Open
Conversation
…laim: the routing guard's refusal quotes each matched verb with its surrounding words, and tests pin its verdict for narrated past work and for the non-git sense of "merge". Review lane: behavior Safety invariant: Refuse-or-allow behavior is unchanged; only the message gains detail. Tests assert the current verdicts rather than changing them. Effectiveness measurement: A test asserts each matched span appears verbatim in the refusal; removing the span from the message fails it. Slice rationale: One claim about one file's output. Replacing prompt matching with a check on the command actually run is a separate, already-open change. Architectural effect: The detector's return value carries the matched text alongside the label, and the message formatter prints it. Goal: Make a refusal say what it matched. Motivation: The author guessed twice about why a spawn was refused, and both guesses were wrong, because the refusal named only verb labels. Alternative considerations: Landing the open change that gates the publishing act instead of the prompt text is the structural fix and is preferred; this slice is the smaller one that helps whatever remains. Widening the negation or noun classes was rejected: the verdicts here are arguably correct, and the real defect is that the author could not see them. Implementation details: In engine/hooks/agent-routing-guard/detect.py, have the verb-collecting function return each hit as a label plus the matched text plus a short window of surrounding characters, and have the refusal formatter list them one per line; keep the existing decision logic, exit codes, and the summary wording that names the skill to follow. Update every caller and test that consumes the old shape. Add fixtures in engine/hooks/agent-routing-guard/tests/test_hooks.py for a narrated past-tense sentence about a force-push, for "merge" used about findings, and for a sentence with no publication verb, each asserting the verdict and, where refused, that the matched words appear in the message. Non-goals: No change to which prompts are refused. No new verb classes, no negation or noun-context changes, and no change to the routing advice the message gives. Layer: domain Feature state: active Files: - engine/hooks/agent-routing-guard/detect.py - engine/hooks/agent-routing-guard/tests/test_hooks.py Change types: - engine/hooks/agent-routing-guard/detect.py: modify - engine/hooks/agent-routing-guard/tests/test_hooks.py: modify Acceptance criteria: - `python3 -m unittest discover -s engine/hooks/agent-routing-guard/tests -v` exits 0. - `python3 scripts/check_hook_test_coverage.py engine/hooks/agent-routing-guard` exits 0. - `python3 scripts/check_no_new_comments.py --base origin/main` exits 0. Exit code: 0 Invoker-Finalize-Id: e1386d6d-295a-4de7-8c40-bb51229bb8e0
…m: the guard's tests and its hook-coverage gate pass. Review lane: proof Safety invariant: Verification is read-only and changes no file. Effectiveness measurement: The commands are the measurement. Slice rationale: One proof for one claim. Architectural effect: None; verification only. Goal: Prove the guard's suite passes with the new message and fixtures. Motivation: A message change that breaks a caller must be caught here. Alternative considerations: The whole suite was rejected as slower without adding evidence for this claim. Implementation details: Run the guard's tests and its coverage gate. Non-goals: No edits. Layer: app_regression Feature state: active Acceptance criteria: - Exits 0 only when the tests and the gate pass. Exit code: 0 Invoker-Finalize-Id: a582d341-dd65-404f-ad0a-dc236a2d3420
…no ephemeral handoff files remain in the worktree. Review lane: cleanup Safety invariant: The scrub script only checks for known handoff artifact names and never touches source or tests. Effectiveness measurement: The script exits non-zero if any handoff artifact remains. Slice rationale: Required terminal scrub for every implementation workflow. Architectural effect: None; hygiene only. Goal: Leave the branch free of handoff artifacts. Motivation: Handoff files must not reach the PR. Alternative considerations: Manual cleanup was rejected as non-deterministic. Implementation details: Run scripts/scrub-handoff-artifacts.sh. Non-goals: No product edits. Layer: app_regression Feature state: active Acceptance criteria: - `bash scripts/scrub-handoff-artifacts.sh` exits 0. Exit code: 0 Invoker-Finalize-Id: a87dbb7c-e3ed-410f-989d-01ae6209b001
…aec4f354e-471f44e0 — Review claim: no ephemeral handoff files remain in the worktree. Review lane: cleanup Safety invariant: The scrub script only checks for known handoff artifact names and never touches source or tests. Effectiveness measurement: The script exits non-zero if any handoff artifact remains. Slice rationale: Required terminal scrub for every implementation workflow. Architectural effect: None; hygiene only. Goal: Leave the branch free of handoff artifacts. Motivation: Handoff files must not reach the PR. Alternative considerations: Manual cleanup was rejected as non-deterministic. Implementation details: Run scripts/scrub-handoff-artifacts.sh. Non-goals: No product edits. Layer: app_regression Feature state: active Acceptance criteria: - `bash scripts/scrub-handoff-artifacts.sh` exits 0.
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_5319b103-66a8-4009-8932-df2985c1429b) |
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.
Summary
A checker blocks local workers from publishing code when a managed workflow should run the work instead.
Its refusal showed only broad action labels. Authors could not see which words caused the refusal.
The checker discarded the matched text after it classified each action.
It now includes each match and nearby text in the refusal. Tests preserve the current decisions for past-work and non-code wording.
Review Claim
Approve refusals that show each matched word with nearby text without changing which requests the checker blocks.
Review Lane
behavior
Review Unit
engine-runtime
Safety Invariant
The checker uses the same matching rules and block-or-allow decision. Only the refusal text changes.
Assumptions: This safety invariant is unconfirmed. The final-only output requirement prevents a confirmation question.
Slice Rationale
This slice changes one refusal message and its data shape. A separate change can check the command that runs instead of request wording.
Non-goals
Architecture
Before
flowchart LR A["Worker request"] --> B["Classify publication actions"] B --> C["Action labels"] C --> D["Format refusal"]After
flowchart LR A["Worker request"] --> B["Classify publication actions"] B --> C["Action labels, matched text, and context"] C --> D["Format refusal"]Test Plan
Test Plan
python3 engine/skills/make-pr/scripts/preflight.py --base origin/main— preflight passed for the engine runtime unit.python3 -m unittest discover -s engine/hooks/agent-routing-guard/tests -v && python3 scripts/check_hook_test_coverage.py engine/hooks/agent-routing-guard— 31 tests passed, and hook coverage passed.python3 scripts/check_no_new_comments.py --base origin/main— no new comments found.bash scripts/scrub-handoff-artifacts.sh— no handoff artifacts found.node scripts/validate-pr-body-local.mjs --body-file /tmp/routing-guard-refusal-pr.md --base main— PR body validation passed.Revert Plan
Revert Plan
git revert <merge-commit-sha>Note
Low Risk
Hook decision logic is untouched; only refusal formatting and the internal hit type change, with tests asserting unchanged classifications.
Overview
When the agent-routing-guard blocks an Agent spawn for publication wording, refusals now quote what matched, not only verb labels like
commit, push.publication_verbsreturnsPublicationVerbHitrecords (label, matched substring, ±40 char context).block_messageandunchecked_messageinsert{hits}lines such ascommit: "…"before the existing routing guidance. Block vs allow logic is unchanged—same regex and negation/noun rules; only stderr copy is richer.Tests add
publication_labelsfor label-only assertions, lock in a few edge-case verdicts, and assert each hit’smatched_textand window appear in the entrypoint refusal.Reviewed by Cursor Bugbot for commit 58ebfaa. Bugbot is set up for automated code reviews on this repo. Configure here.