Skip to content

governance-reusable's new actions: read startup_failures every caller granting only contents:read #527

Description

@hyperpolymath

Summary

0ced540e ("chore: estate-wide security compliance", 2026-07-26) added actions: read to
governance-reusable.yml's own permissions: block. A called reusable workflow cannot
request more permissions than its caller grants
— so every caller whose wrapper grants
only contents: read is now rejected before any job starts.

Symptom is startup_failure with zero jobs and no check run, which is materially harder
to diagnose than a normal red: there is nothing to click into, and gh pr checks does not
surface it.

hyperpolymath/standards' own main is currently red, so this is not confined to callers.

Evidence

The upstream change:

 permissions:
+  actions: read
   contents: read

A representative caller (hyperpolymath/gnpl/.github/workflows/governance.yml) before the fix:

permissions:
  contents: read

jobs:
  governance:
    uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@main

Timeline in gnpl:

When What
2026-07-21 Governance success
2026-07-26T13:53Z 0ced540e lands upstream
2026-07-27 next Governance run → startup_failure, 0 jobs

.github/ was byte-identical to main across that window — the only variable is the
reusable.

Note the amplifier: callers pin this reusable at @main, not a SHA, so a permissions
change propagates to the whole estate the moment it merges, with no staged rollout.

Suggested fix (either)

  1. Preferred — don't require it at the caller. Move the actions: read requirement to
    the specific job inside the reusable that needs it (job-level permissions: are
    allowed to be narrower and do not impose on callers), or drop it if the underlying step
    can work without reading the Actions API.
  2. Otherwise — announce and sweep. If callers genuinely must grant it, the caller
    wrappers need actions: read added across the estate in the same change, since the
    reusable's requirement is not satisfiable retroactively.

Option 1 avoids a breaking interface change to a workflow that ~300 repos call.

Local status

gnpl is unblocked by mirroring the permission
(hyperpolymath/gnpl#7) — filing here because the same one-line break applies to every other
caller, and fixing it N times is the wrong shape.

Related from earlier passes: #516 (Hypatia ImageOS=ubuntu24), #520 (scorecard.yml
template vs the SARIF policy).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions