From b980e39b22b3c69e1306dbfe12033c506fad8461 Mon Sep 17 00:00:00 2001 From: topcoder1 Date: Tue, 12 May 2026 17:58:47 -0700 Subject: [PATCH 1/3] feat(coverage-floor): use AUTOMERGE_PAT to open seed PR (workflows spawn naturally) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous redesign (#55) moved the seed self-commit from the install PR into a post-merge follow-up PR — but the new PR is opened via GITHUB_TOKEN, which has the SAME recursion-prevention problem: pull _request workflows don't spawn on GITHUB_TOKEN-opened PRs. The seed PR sits "blocked" until a user pushes an empty commit to its branch. Caught on wxa_vpn#361: seed PR opened cleanly at 80.2% (measured 80.7%), but the rollup showed zero required-check runs and merge=BLOCKED. Required manual empty-commit unblock, same as the original self-commit design. Fix: forward AUTOMERGE_PAT (already deployed fleet-wide for claude-author-automerge.yml's same recursion-prevention workaround) into the reusable. Use it to push the seed branch AND to open the seed PR. PAT-opened PRs DO spawn pull_request workflows naturally — no manual unblock needed. Behavior: - AUTOMERGE_PAT present (caller forwards it): use PAT for push + PR open. Seed PR spawns all required checks normally. - AUTOMERGE_PAT absent (caller didn't forward): fall back to GITHUB_TOKEN with a clear ::warning:: that manual unblock will be needed. This makes the install still functional, just less smooth. Caller-side change: each fleet repo's coverage-floor.yml caller must forward the secret: secrets: AUTOMERGE_PAT: ${{ secrets.AUTOMERGE_PAT }} The dotclaude caller template (templates/ci-workflows/callers/ coverage-floor.yml) will be updated in a companion PR. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/coverage-floor.yml | 30 ++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/.github/workflows/coverage-floor.yml b/.github/workflows/coverage-floor.yml index 09a538d..9c857e9 100644 --- a/.github/workflows/coverage-floor.yml +++ b/.github/workflows/coverage-floor.yml @@ -87,10 +87,14 @@ on: required: false type: string default: "" + secrets: + AUTOMERGE_PAT: + description: "PAT used to push the seed branch and open the seed PR so that pull_request workflows actually fire (GITHUB_TOKEN doesn't trigger them — recursion prevention). Already deployed fleet-wide for `claude-author-automerge.yml`. Caller must explicitly forward: `secrets: AUTOMERGE_PAT: ${{ secrets.AUTOMERGE_PAT }}`. Required for the post-merge seed flow to land without manual unblock." + required: false # absence falls back to GITHUB_TOKEN + warning permissions: - contents: write # seed-mode self-commit - pull-requests: write # sticky PR comment + contents: write # post-merge seed branch + push + pull-requests: write # sticky PR comment + open seed PR jobs: measure: @@ -287,7 +291,12 @@ jobs: TARGET: ${{ steps.floor.outputs.target }} INPUT_FLOOR_FILE: ${{ inputs.floor_file || '.coverage-floor' }} SEED_MIN: ${{ inputs.seed_minimum || '1.0' }} - GH_TOKEN: ${{ github.token }} + # Use AUTOMERGE_PAT if forwarded by the caller; fall back to + # GITHUB_TOKEN with a warning (recursion-prevention means seed + # PR opened by GITHUB_TOKEN won't spawn pull_request workflows, + # so the seed PR will sit "blocked" until a user pushes an + # empty commit to its branch). + AUTOMERGE_PAT: ${{ secrets.AUTOMERGE_PAT }} run: | set -euo pipefail # Loop-prevention guard: refuse to seed below seed_minimum. @@ -300,6 +309,19 @@ jobs: PROPOSED=$(awk -v m="$MEASURED" 'BEGIN {x = m - 0.5; if (x < 0) x = 0; printf "%.1f", x}') TODAY=$(date -u +%Y-%m-%d) + # Select the auth token. Prefer AUTOMERGE_PAT (the seed PR will + # spawn pull_request workflows naturally); fall back to GITHUB_TOKEN + # with a warning so the operator knows manual unblock is needed. + if [[ -n "${AUTOMERGE_PAT:-}" ]]; then + export GH_TOKEN="$AUTOMERGE_PAT" + PUSH_URL="https://x-access-token:${AUTOMERGE_PAT}@github.com/${GITHUB_REPOSITORY}.git" + echo "==> Using AUTOMERGE_PAT for seed branch push + PR open (workflows will fire naturally)" + else + export GH_TOKEN="${{ github.token }}" + PUSH_URL="origin" + echo "::warning::AUTOMERGE_PAT secret not forwarded by caller. Falling back to GITHUB_TOKEN, which won't spawn pull_request workflows on the seed PR. The seed PR will be 'blocked' until someone pushes an empty commit to its branch. Forward the secret in the caller: 'secrets: AUTOMERGE_PAT: \${{ secrets.AUTOMERGE_PAT }}'" + fi + # Ensure the label exists (idempotent). gh label create coverage-floor-seed \ --description "Auto-opened by coverage-floor.yml to seed the floor after install" \ @@ -323,7 +345,7 @@ jobs: git add "$INPUT_FLOOR_FILE" git commit -m "chore(coverage): seed floor at ${PROPOSED}% (measured ${MEASURED}%)" \ -m "Auto-seeded post-merge by coverage-floor.yml. After this PR merges, future PRs enforce this floor." - git push -u origin "$BRANCH" + git push -u "$PUSH_URL" "$BRANCH" # Open auto-mergeable PR. Single .coverage-floor file change is # safe-paths-automerge eligible per the central policy. From 0bc4ac01e3534926d910524c8d6ab0e5e9da8ebc Mon Sep 17 00:00:00 2001 From: topcoder1 Date: Tue, 12 May 2026 18:14:21 -0700 Subject: [PATCH 2/3] fix(coverage-floor): escape ${{ }} in description/warning strings actionlint parses the secret-input description as a GitHub Actions expression context and flags '${{ secrets.X }}' as 'context secrets not allowed here'. Escape with '$${{ }}' so the literal renders in the description without actionlint evaluating it. Same fix applied to the fallback warning message (was using bash '\${{ }}' escape which actionlint still tried to parse). --- .github/workflows/coverage-floor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/coverage-floor.yml b/.github/workflows/coverage-floor.yml index 9c857e9..5020d2b 100644 --- a/.github/workflows/coverage-floor.yml +++ b/.github/workflows/coverage-floor.yml @@ -89,7 +89,7 @@ on: default: "" secrets: AUTOMERGE_PAT: - description: "PAT used to push the seed branch and open the seed PR so that pull_request workflows actually fire (GITHUB_TOKEN doesn't trigger them — recursion prevention). Already deployed fleet-wide for `claude-author-automerge.yml`. Caller must explicitly forward: `secrets: AUTOMERGE_PAT: ${{ secrets.AUTOMERGE_PAT }}`. Required for the post-merge seed flow to land without manual unblock." + description: "PAT used to push the seed branch and open the seed PR so that pull_request workflows actually fire (GITHUB_TOKEN doesn't trigger them — recursion prevention). Already deployed fleet-wide for `claude-author-automerge.yml`. Caller must explicitly forward: `secrets: AUTOMERGE_PAT: $${{ secrets.AUTOMERGE_PAT }}`. Required for the post-merge seed flow to land without manual unblock." required: false # absence falls back to GITHUB_TOKEN + warning permissions: @@ -319,7 +319,7 @@ jobs: else export GH_TOKEN="${{ github.token }}" PUSH_URL="origin" - echo "::warning::AUTOMERGE_PAT secret not forwarded by caller. Falling back to GITHUB_TOKEN, which won't spawn pull_request workflows on the seed PR. The seed PR will be 'blocked' until someone pushes an empty commit to its branch. Forward the secret in the caller: 'secrets: AUTOMERGE_PAT: \${{ secrets.AUTOMERGE_PAT }}'" + echo "::warning::AUTOMERGE_PAT secret not forwarded by caller. Falling back to GITHUB_TOKEN, which won't spawn pull_request workflows on the seed PR. The seed PR will be 'blocked' until someone pushes an empty commit to its branch. Forward the secret in the caller: 'secrets: AUTOMERGE_PAT: $\${{ secrets.AUTOMERGE_PAT }}'" fi # Ensure the label exists (idempotent). From 20d11158b8a5b032182c08ea0607f52eb4519007 Mon Sep 17 00:00:00 2001 From: topcoder1 Date: Tue, 12 May 2026 18:18:44 -0700 Subject: [PATCH 3/3] fix(coverage-floor): drop ${{ }} literals from description/warning actionlint flags `${{ secrets.X }}` inside string literals regardless of escape attempts. Both `\${{ }}` (bash) and `$${{ }}` (GH expression escape) still trip the parser. Simpler: don't put the literal templating syntax in the description at all. Point readers to the caller template in topcoder1/dotclaude for the exact syntax. --- .github/workflows/coverage-floor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/coverage-floor.yml b/.github/workflows/coverage-floor.yml index 5020d2b..a605f38 100644 --- a/.github/workflows/coverage-floor.yml +++ b/.github/workflows/coverage-floor.yml @@ -89,7 +89,7 @@ on: default: "" secrets: AUTOMERGE_PAT: - description: "PAT used to push the seed branch and open the seed PR so that pull_request workflows actually fire (GITHUB_TOKEN doesn't trigger them — recursion prevention). Already deployed fleet-wide for `claude-author-automerge.yml`. Caller must explicitly forward: `secrets: AUTOMERGE_PAT: $${{ secrets.AUTOMERGE_PAT }}`. Required for the post-merge seed flow to land without manual unblock." + description: "PAT used to push the seed branch and open the seed PR so that pull_request workflows actually fire (GITHUB_TOKEN doesn't trigger them — recursion prevention). Already deployed fleet-wide for `claude-author-automerge.yml`. Caller must explicitly forward this secret (see callers/coverage-floor.yml in topcoder1/dotclaude for the syntax). Required for the post-merge seed flow to land without manual unblock." required: false # absence falls back to GITHUB_TOKEN + warning permissions: @@ -319,7 +319,7 @@ jobs: else export GH_TOKEN="${{ github.token }}" PUSH_URL="origin" - echo "::warning::AUTOMERGE_PAT secret not forwarded by caller. Falling back to GITHUB_TOKEN, which won't spawn pull_request workflows on the seed PR. The seed PR will be 'blocked' until someone pushes an empty commit to its branch. Forward the secret in the caller: 'secrets: AUTOMERGE_PAT: $\${{ secrets.AUTOMERGE_PAT }}'" + echo "::warning::AUTOMERGE_PAT secret not forwarded by caller. Falling back to GITHUB_TOKEN, which won't spawn pull_request workflows on the seed PR. The seed PR will be 'blocked' until someone pushes an empty commit to its branch. See callers/coverage-floor.yml in topcoder1/dotclaude for how to forward the secret." fi # Ensure the label exists (idempotent).