Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/pr-review-merge-scheduler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ jobs:
contents: write
id-token: write
pull-requests: write
statuses: read
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
GH_TOKEN: ${{ github.token }}
Expand Down
38 changes: 38 additions & 0 deletions docs/doctoring/scheduler-status-read-permission.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Scheduler commit-status read permission (#2120)

The organization-required scheduler selected `github.token` for same-repository
reads but omitted `statuses` from `scan-pr-queue.permissions`. The private
consumer's combined-status GET consequently failed with HTTP 403 before a merge
verdict. `checks: read` does not grant classic commit-status access.

Source baseline: `fb17ef556f94f673234aa557254ae52779e9a7b0`.
Consumer evidence: ContextualWisdomLab/late-life-anxiety-reanalysis#10,
head `3d1e3ae56e3ef6ca0a995b6082c4f4a13629e0f6`, run `34698738407`,
job `103566634488` (2026-09-12). The reported failing endpoint is
`GET /repos/{repository}/commits/{head}/status`.

The repair adds only `statuses: read` to the existing scan job. Workflow defaults,
mutation credentials, cross-repository credential selection and fail-closed API
errors remain intact. It adds no status publication or App installation grant.
The existing credential-contract test now requires exactly `read` in that job's
permission block; its RED revision is `9521b6771`.

Validation uses the existing pytest workflow/credential/status suites and
Actionlint's workflow validation. Local tests prove the declared contract, not a
hosted permission grant. After protected integration, validate a newly loaded
central source SHA and the consumer's exact current head: the combined-status
request must succeed, and missing checks or substantive failures must still block
merge. For reusable callers, every caller permission ceiling must also admit
status reads; the inspected consumer PR head has no `.github` tree, so do not
invent a repository-local caller or modify App permissions to compensate.

Next integration review: 2026-09-13, because this prevents the current private
consumer's mandatory scheduler from evaluating status evidence. #2116's HWPX
classification remains a separate bootstrap repair. Reverting this one-line grant
restores the pre-existing 403 behavior; it is not a viable consumer repair.

## Reference

GitHub. (n.d.). *REST API endpoints for commit statuses: Get the combined status
for a specific reference*. Retrieved September 12, 2026, from
https://docs.github.com/en/rest/commits/statuses#get-the-combined-status-for-a-specific-reference
5 changes: 5 additions & 0 deletions tests/test_opencode_agent_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -2371,6 +2371,11 @@ def test_merge_scheduler_uses_escalating_mutation_credentials():
encoding="utf-8"
)

scan_job = workflow.split(" scan-pr-queue:\n", 1)[1]
permission_block = scan_job.split(" permissions:\n", 1)[1].split(" env:\n", 1)[0]
status_permissions = re.findall(r"^ statuses: (\w+)\s*$", permission_block, re.MULTILINE)
assert status_permissions == ["read"], "same-repository status evidence needs read-only permission"

assert "id-token: write" in workflow
assert "Exchange OpenCode app token for scheduler mutations" in workflow
assert "secrets.PR_REVIEW_MERGE_TOKEN" in workflow
Expand Down
Loading