Worker cannot push .github/workflows/* — missing workflow scope

Trigger: a queue worker’s git push is rejected with:

! [remote rejected] feat/... -> feat/...
  (refusing to allow an OAuth App to create or update workflow
   `.github/workflows/<file>.yml` without `workflow` scope)

The commit itself succeeds — only the push is refused, and only when the diff touches .github/workflows/. Any other path pushes fine, which is why this hits so rarely and looks confusing when it does.

Why

Workers authenticate git via gh auth git-credential as the machine account (e.g. AI-Dev-IO1). That login is an OAuth token (gho_…) whose scopes are gist, read:org, repo — no workflow. GitHub refuses workflow-file writes from any OAuth token lacking that scope. Confirm with:

gh auth status            # look at "Token scopes:" — no `workflow` = this playbook applies

Do not try to fix this by re-scoping the machine account interactively; the worker is headless.

Fix — push with the SOPS GH_TOKEN (has Workflows:write)

GH_TOKEN in secrets/monitoring.env.sops is a radieu fine-grained PAT that carries the Workflows write permission. Use it for the push only; leave the gh login alone so gh issue / gh pr keep working as the machine account.

GIT_ASKPASS is the safe delivery mechanism: the token stays in the environment and never reaches argv, git config, or the remote URL. Never embed a token in the remote URL — that is exactly how incident #4047 leaked a PAT at rest into /home/claude-runner/app.

export SOPS_AGE_KEY_FILE=/home/claude-runner/.age/p24-infra-keys.txt
 
cat > /tmp/askpass.sh <<'EOF'
#!/usr/bin/env bash
case "$1" in
  Username*) echo "x-access-token" ;;
  *)         echo "$GIT_PW" ;;
esac
EOF
chmod +x /tmp/askpass.sh
 
TOK=$(sops --decrypt --input-type dotenv --output-type dotenv secrets/monitoring.env.sops \
  | grep -m1 '^GH_TOKEN=' | cut -d= -f2- | tr -d '"' | tr -d '\r')
 
GIT_PW="$TOK" GIT_ASKPASS=/tmp/askpass.sh GIT_TERMINAL_PROMPT=0 \
  git -c credential.helper= push https://github.com/radieu/p24-infra.git \
  HEAD:refs/heads/<your-branch>
 
unset TOK GIT_PW; rm -f /tmp/askpass.sh

-c credential.helper= is required — without it the gh helper supplies the scopeless gho_ token and the push is rejected again.

After pushing — verify nothing leaked

git config --get remote.origin.url                      # must be tokenless
git config --local --list | grep -icE 'gh[pousr]_|github_pat_'   # must be 0

Scrub any push output before quoting it anywhere: sed -E 's/(gh[pousr]_|github_pat_)[A-Za-z0-9_]*/\1***/g'

Notes

  • gh pr create / gh issue comment need no special scope — only the push does. Once the branch is up, the rest of the worker flow proceeds as the machine account normally.
  • The same applies to merging a PR whose head branch adds workflow files: the merge is performed server-side by GitHub, so it needs no workflow scope from the worker.
  • If GH_TOKEN is ever rotated to a token without Workflows:write, this breaks. The symptom is identical to the above; re-check with the gh auth status / push test before assuming otherwise.

Related: adding-new-worker.md · incident #4047 (PAT in remote URL)