diff --git a/tools/test_triage_daemon.py b/tools/test_triage_daemon.py index 04ad5255b..8c1726a49 100644 --- a/tools/test_triage_daemon.py +++ b/tools/test_triage_daemon.py @@ -775,6 +775,18 @@ def test_tells_the_agent_to_report_a_denial_rather_than_print_findings(self): completed review in the log. The prompt asks for a denial to be stated plainly.""" self.assertIn("denied", triage_daemon.GH_API_ENDPOINT_FIRST_PROMPT) + def test_closes_the_skills_print_instead_fallback(self): + """/code-review --comment prefers mcp__github_inline_comment__create_inline_comment, and when + that is missing offers "fall back to gh api ... or print the findings instead". The tool is + never loaded here (--strict-mcp-config), so every review lands on that sentence, and PR + #5283's run took the print branch without ever trying gh api - no denial to report, so the + denial rule above never applied. The prompt must name the tool as absent and make gh api the + required path, with printing allowed only once a gh api call has actually been denied.""" + prompt = triage_daemon.GH_API_ENDPOINT_FIRST_PROMPT + self.assertIn("mcp__github_inline_comment__create_inline_comment", prompt) + self.assertIn("never available", prompt) + self.assertIn("not an acceptable substitute", prompt) + def test_steers_comment_bodies_into_a_scratch_file(self): """PR #5229's review had its endpoint-first POST denied because the body was an inline double-quoted argument holding backticks: the shell reads those as command substitution, diff --git a/tools/triage_daemon.py b/tools/triage_daemon.py index 2dec58e71..d2db8d403 100644 --- a/tools/triage_daemon.py +++ b/tools/triage_daemon.py @@ -470,7 +470,11 @@ # a finished review in the log. It also moves comment bodies into a scratch file: PR #5229's # POSTs were endpoint-first and still denied, because a double-quoted body holding backticks is # command substitution to the shell, and the permission check denies the substituted commands -# that no rule allows. Also carries the bot-disclosure requirement for these two flows: +# that no rule allows. It also closes /code-review's own escape hatch: with its preferred +# mcp__github_inline_comment tool missing (never loaded, see --strict-mcp-config), the skill +# says "fall back to gh api ... or print the findings instead", and PR #5283's run (2026-09-28) +# took the print branch without trying gh api at all - nothing was denied, so the denial rule +# alone never fired. Also carries the bot-disclosure requirement for these two flows: # /code-review's own instructions live in a skill we don't own, so this prompt is the only # lever available for it; /pr-cleanup's SKILL.md already asks for disclosure directly, and # this is the belt-and-braces backup for it, same reasoning as the endpoint-first steer. @@ -524,6 +528,10 @@ ) GH_API_ENDPOINT_FIRST_PROMPT = ( + "The `mcp__github_inline_comment__create_inline_comment` tool is never available in this session, so post every PR comment " + "with `gh api` - that is the required path, not one option among several. Printing the findings instead of posting them is " + "not an acceptable substitute, even where a skill's instructions offer it as a fallback; it is permitted only after a `gh api` " + "call has actually been denied. " "Permission rules in this session match a literal command prefix, so `gh api` calls are only permitted when the current allowlist covers the exact spelling you use. " "Prefer the endpoint-first, unquoted form (endpoint immediately after `gh api`) and put flags after the endpoint - for example " f"`gh api repos/{REPO}/pulls/123/comments --method POST -f path=apps/predbat/example.py -F body=@{SCRATCH_DIR}/comment-1.md`. "