fix(review): grant checks and statuses read for the CI rollup - #25
Conversation
There was a problem hiding this comment.
Permission fix is correct and minimal: checks and statuses are both valid job-level scopes, and since the job already declares permissions: explicitly, adding two reads takes nothing away. Verified the new expression scan against both workflow files by hand — the only ${{ }} empty pair in the tree is the YAML comment at claude-pr-review.yml:587, which sits outside a block scalar and is correctly skipped, and the two interpolations inside the run: block at lines 114-115 are the two context-step-test.sh substitutes, so its new no-exemption grep passes.
Five non-blocking comments inline; the two worth acting on are actionlint never actually running in CI (not preinstalled on ubuntu-latest) and the permission table failing open when step_script extraction drifts.
CI was still queued when this review started, so I have no result for either check.
| check_permission() { | ||
| printf '%s\n' "$step_script" | grep -qF -- "$1" || return 0 | ||
| if printf '%s\n' "$declared" | grep -qx "$2"; then | ||
| echo "ok $2 is declared for $3" |
There was a problem hiding this comment.
super nit: (not blocking) check_permission verifies the permission key is present, not that it grants a read. declared comes from print $1 on lines like issues: read, so it holds issues regardless of the value, and grep -qx issues matches just as happily when the workflow says issues: none.
none is not hypothetical here — it is exactly what someone reaches for when tightening a job that declares permissions: explicitly, and it is a one-word edit away from read. In that case this file prints ok issues is declared for the PR conversation comments while the read 403s into "Could not read PR conversation comments." — the same silent-empty-block failure the table exists to catch, now with an affirmative "ok" in front of it.
Capturing the value instead of just the key covers it, accepting write since it implies read:
declared=$(awk '/^ permissions:$/ { p = 1; next } p && /^ [a-z-]+: *(read|write) *$/ { print $1 } p && /^ [a-z]/ { exit }' \
"$WORKFLOW_FILE" | tr -d ':')id-token: write and the rest still pass; checks: none stops passing.
The first production run reached the model with every block intact except the one this change exists for: the CI block said
Could not read check status.on a PR with 12 checks.gh pr view --json statusCheckRollupresolves a GraphQL field needingchecks: readandstatuses: read, and the job grants neither, so the whole rollup fails with "Resource not accessible by integration" rather than returning the half it may read. A personal access token has every scope, so no local run could reproduce it.Also adds
tests/workflow-lint-test.sh, wired first in the suite:run:block (part of the string value, parsed, and fatal when empty). Scanning raw text without that distinction reports the harmless one.checks: readand watching it fail.tests/context-step-test.shno longer exempts comments from its delimiter check — that exemption is what let the outage ship.