From 267686416346ae05b99bf8d9e2062fb56f4377c1 Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Sun, 19 Jul 2026 20:46:29 -0400 Subject: [PATCH] Fix AI workflow allowlists being destroyed by tokenization (#4560) --- .github/workflows/marvin-dedupe-issues.yml | 2 +- .github/workflows/marvin-label-triage.yml | 35 +++++++++++++++++----- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/.github/workflows/marvin-dedupe-issues.yml b/.github/workflows/marvin-dedupe-issues.yml index 5815f98ed..214414513 100644 --- a/.github/workflows/marvin-dedupe-issues.yml +++ b/.github/workflows/marvin-dedupe-issues.yml @@ -100,7 +100,7 @@ jobs: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY_FOR_CI }} allowed_non_write_users: "*" claude_args: | - --allowedTools Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh api:*),Bash(gh issue comment:*),Task + --allowedTools "Bash(gh issue view:*)","Bash(gh search:*)","Bash(gh issue list:*)","Bash(gh api:*)","Bash(gh issue comment:*)",Task settings: | { "model": "claude-sonnet-4-6", diff --git a/.github/workflows/marvin-label-triage.yml b/.github/workflows/marvin-label-triage.yml index 18bbf767a..6c77f6f59 100644 --- a/.github/workflows/marvin-label-triage.yml +++ b/.github/workflows/marvin-label-triage.yml @@ -153,7 +153,7 @@ jobs: allowed_non_write_users: "*" allowed_bots: "marvin-context-protocol" claude_args: | - --allowedTools Bash(gh label list:*),Bash(bash .github/scripts/triage-label.sh:*),mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__add_issue_comment,mcp__github__get_pull_request_files + --allowedTools "Bash(gh label list:*)","Bash(bash .github/scripts/triage-label.sh:*)",mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__add_issue_comment,mcp__github__get_pull_request_files settings: | { "model": "claude-sonnet-4-6", @@ -174,19 +174,38 @@ jobs: EXECUTION_FILE: ${{ steps.marvin.outputs.execution_file }} run: | file="${EXECUTION_FILE:-}" - if [[ -z "$file" || ! -f "$file" ]]; then + if [[ -z "$file" || ! -s "$file" ]]; then file="${RUNNER_TEMP}/claude-execution-output.json" fi - if [[ ! -f "$file" ]]; then - echo "::warning::No Marvin execution log found; cannot verify tool permissions." - exit 0 + # A missing or empty log means we cannot tell a clean run from a + # blocked one, which is the exact failure this step exists to catch. + if [[ ! -s "$file" ]]; then + echo "::error::No Marvin execution log found; cannot verify tool permissions." + exit 1 fi - # -s so this reads both a JSON array and a stream of JSON objects. - denials=$(jq -s '[.. | objects | .permission_denials_count? // empty] | add // 0' "$file" 2>/dev/null || echo 0) + # The persisted log carries a `permission_denials` array on each + # `type: result` entry; the `permission_denials_count` scalar only + # appears in the action's condensed stdout summary, never on disk. + # Anchor to result entries rather than recursing with `..`, which + # descends into each denial's `tool_input` and double-counts any + # denied command that happens to mention the field name. + if ! denials=$(jq -s ' + [ .[] | if type == "array" then .[] else . end ] + | map(select(type == "object" and .type == "result")) + | map( + [ (.permission_denials | if type == "array" then length else 0 end), + (.permission_denials_count | if type == "number" then . else 0 end) ] + | max + ) + | add // 0 + ' "$file"); then + echo "::error::Could not parse Marvin execution log ($file)." + exit 1 + fi echo "Permission denials: $denials" if [[ "$denials" -gt 0 ]]; then - echo "::error::Marvin was denied $denials tool call(s), so triage likely applied no labels. Check the --allowedTools allowlist in this workflow (Bash patterns need a ':*' suffix to permit arguments)." + echo "::error::Marvin was denied $denials tool call(s), so triage likely applied no labels. Check the --allowedTools allowlist in this workflow: any Bash(...) pattern containing a space must be individually quoted, or it gets torn apart by whitespace splitting before it reaches the permission matcher." exit 1 fi