From c89715be8408a15f4e5c64d5e6afd8df925b1c55 Mon Sep 17 00:00:00 2001 From: Dan Barr <6922515+danbarr@users.noreply.github.com> Date: Tue, 30 Jun 2026 17:17:13 -0400 Subject: [PATCH] Fix Slack notify prompt to use literal values, not $VAR Claude Code's permission matcher denies any Bash command containing an unexpanded shell variable (e.g. "$PR_NUMBER"), regardless of whether the rest of the command matches an allowedTools wildcard like Bash(gh:*). The prompt told Claude to build its gh command from env vars using shell expansion, so every attempt was denied no matter how it was quoted, and Claude burned all 30 turns retrying variations before timing out. Interpolate the PR number, repo, URL, and workspace path directly into the prompt text via GitHub Actions expressions instead, so the Bash command Claude runs contains literal values with no expansion. None of these are PR-content-controlled (they come from trusted event/repository context), so embedding them is safe. Verified locally: ran the rendered prompt through the claude CLI with the same allowedTools/model/max-turns and got num_turns=3, permission_denials=0, and a correct output file. --- .github/workflows/autogen-docs-notify.yml | 27 ++++++++--------------- 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/.github/workflows/autogen-docs-notify.yml b/.github/workflows/autogen-docs-notify.yml index 6adc0f71..3101f75d 100644 --- a/.github/workflows/autogen-docs-notify.yml +++ b/.github/workflows/autogen-docs-notify.yml @@ -102,10 +102,6 @@ jobs: # nor make arbitrary network calls (no curl/Bash beyond gh). - name: Compose reviewer summary uses: anthropics/claude-code-action@51705da45eecce209d4700538bf8377d5b5fc695 # v1.0.152 - env: - PR_NUMBER: ${{ github.event.pull_request.number }} - GH_REPO: ${{ github.repository }} - PR_URL: ${{ github.event.pull_request.html_url }} with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} # Skips claude-code-action's OIDC -> GitHub App token exchange @@ -140,16 +136,11 @@ jobs: the `gh` CLI. A later, separate, deterministic step does the actual Slack posting. - Context (available to you as environment variables): - PR_NUMBER - the PR number - GH_REPO - owner/repo of this repository - PR_URL - the PR's html_url - MESSAGE_FILE - the path to write the JSON output file to - GH_TOKEN - auth for the `gh` CLI - STEP 1 — Read the PR. - Run: - gh pr view "$PR_NUMBER" --repo "$GH_REPO" --json title,body,reviewRequests,files,url + Run this exact command (the values below are already + filled in -- do not substitute shell variables, and do + not quote the numbers or the repo name): + gh pr view ${{ github.event.pull_request.number }} --repo ${{ github.repository }} --json title,body,reviewRequests,files,url From the JSON, extract: - the PR title - the PR body. The body contains a marker-delimited @@ -164,16 +155,16 @@ jobs: are GitHub logins). STEP 2 — Write the message content as JSON. - Use the **Write** tool to write a file at the path given by - the env var MESSAGE_FILE. The file MUST be valid JSON with - EXACTLY this shape: + Use the **Write** tool to write a file at exactly this path: + ${{ github.workspace }}/.autogen-docs-slack-message.json + The file MUST be valid JSON with EXACTLY this shape: { "headline": "", "summary_bullets": ["...", "..."], "reviewers": [ {"login": "", "note": ""} ], - "pr_url": "" + "pr_url": "${{ github.event.pull_request.html_url }}" } Rules: - "headline" is plain text only (NO Slack/markdown link @@ -191,7 +182,7 @@ jobs: have produced any user- or docs-facing changes, which is expected and fine, and in that case their approval simply confirms nothing was missed in the generated docs. - - "pr_url" is the value of the PR_URL env var. + - "pr_url" must be copied exactly as filled in above. Do NOT include any Slack ids, Slack mrkdwn, `<@...>` tags, or `` links — only the plain content above. Do NOT post anything. Do NOT call curl. After writing the file,