diff --git a/.github/workflows/dogfood.yml b/.github/workflows/dogfood.yml index 6bea054..3847d4f 100644 --- a/.github/workflows/dogfood.yml +++ b/.github/workflows/dogfood.yml @@ -19,11 +19,15 @@ on: pull_request_review_comment: types: [created] +# NOTE: `workflows: write` is not a valid permissions scope — GitHub began +# hard-rejecting it ~2026-05-01 ("Unexpected value 'workflows'"), which made +# every push spawn a phantom failed dogfood run and broke /dogfood comments. +# Workflow-file write access is granted by the App's Workflows permission +# (see CONTRIBUTING.md), not by a job permissions key. permissions: contents: write issues: write pull-requests: write - workflows: write jobs: resolve: diff --git a/tests/test_yaml.py b/tests/test_yaml.py index 166b632..b64dc4d 100644 --- a/tests/test_yaml.py +++ b/tests/test_yaml.py @@ -202,17 +202,24 @@ def find_target_checkout(job): ) -def test_dogfood_has_workflows_write_permission(): - """dogfood.yml must include workflows:write so the fallback github.token - can push workflow file changes when no app token is configured. - - Regression test for issue #463. +def test_no_invalid_workflows_permission_key(): + """No workflow may use `workflows:` in a permissions block — it is not a + valid GitHub Actions permissions scope, and since ~2026-05-01 GitHub + hard-rejects it ("Unexpected value 'workflows'"), invalidating the whole + workflow file. It was originally added for issue #463 in the belief it + let github.token push workflow files; GITHUB_TOKEN can never modify + workflow files regardless of permissions — that capability comes from + the GitHub App's Workflows permission (see CONTRIBUTING.md). + + Regression test for the May-August 2026 phantom dogfood failures. """ - dogfood = load_yaml(REPO_ROOT / ".github/workflows/dogfood.yml") - permissions = dogfood.get("permissions", {}) - assert permissions.get("workflows") == "write", ( - "dogfood.yml must have 'workflows: write' in permissions" - ) + for wf in (REPO_ROOT / ".github/workflows").glob("*.yml"): + config = load_yaml(wf) + permissions = config.get("permissions", {}) or {} + assert "workflows" not in permissions, ( + f"{wf.name}: 'workflows' is not a valid permissions scope and " + f"invalidates the workflow file" + ) # --- Security checks ---