feat(ledger): screen open items whose subject already exists on main (BACKLOG #1426) #398
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Required workflow state | |
| # Every REQUIRED status check must belong to a workflow GitHub will actually run. | |
| # | |
| # THE DEFECT THIS EXISTS FOR — measured on the sibling vault repo (wshallwshall/MessageFoundry), | |
| # 2026-07-30: 10 required contexts whose workflows were ALL `disabled_manually`. A disabled workflow | |
| # never dispatches, so those contexts never reported — NO pull request could merge, and every merge | |
| # there had silently been riding admin bypass. Nobody noticed, because the symptom presents as "CI is | |
| # stuck", not as "branch protection is misconfigured". | |
| # | |
| # `tests/test_required_contexts.py` cannot see this: a disabled workflow keeps its file and job name on | |
| # disk, so resolving a context against YAML passes in the healthy AND the broken case. Workflow `state` | |
| # is server-side, so this has to be an API check. | |
| # | |
| # SCHEDULED, not per-PR, deliberately. A workflow disabled AFTER the last pull request is invisible to | |
| # any per-PR check — there is no PR to run it on. The failure arrives while the repo is idle, which is | |
| # exactly when nobody is looking. | |
| # | |
| # It checks REACHABILITY (can the context ever report?), not outcome. Whether a check passes is CI's | |
| # job; whether it is capable of running at all is this one's. | |
| on: | |
| schedule: | |
| # 07:00 UTC — an hour after the nightly CI cron, so a workflow disabled overnight is reported the | |
| # same morning rather than a day later. | |
| - cron: "0 7 * * *" | |
| workflow_dispatch: | |
| # Also on a PR that edits the required set or the workflows it points at: this is the one moment a | |
| # human is actively changing the mapping, and a typo'd context is cheapest to catch right then. | |
| pull_request: | |
| paths: | |
| - ".github/required-contexts.txt" | |
| - ".github/workflows/**" | |
| permissions: | |
| contents: read | |
| jobs: | |
| reachable: | |
| name: required contexts belong to active workflows | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| actions: read # read workflow `state`; NOT the admin scope branch protection would need | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: "3.14" | |
| - name: Install PyYAML (the resolver parses the workflow files) | |
| run: | | |
| # Hash-pinned from the CI toolchain lock, like every other scanner install (ADR 0034 §3). | |
| python -m pip install --require-hashes -r ci/locks/ci-scanners.lock | |
| - name: Reconcile required contexts against workflow state | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: python scripts/ci/check_required_workflow_state.py --repo "$GITHUB_REPOSITORY" | |
| # A SECOND JOB, NOT A SECOND STEP IN THE FIRST. The job above asks REACHABILITY -- can a required | |
| # context ever report? This one asks ACCURACY -- does the checked-in file match the server? They fail | |
| # for different reasons and a reader needs to know which, so they get separate names and separate exit | |
| # codes. Measured 2026-08-30: the server required 15 contexts while the file named 13, and the two it | |
| # omitted were listed in its own header as "DELIBERATELY NOT REQUIRED" -- the file asserted the | |
| # opposite of the server, and every in-repo test still passed because they only compare documents to | |
| # documents. | |
| accurate: | |
| name: the required-contexts file matches the server | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| # NO extra scope. The context list comes from `GET /repos/{owner}/{repo}/branches/{branch}`, which | |
| # answers UNAUTHENTICATED -- verified 2026-08-30, all 15 contexts with no token. `strict` is the | |
| # field that would need admin scope, and this job deliberately does not check it (see the script's | |
| # docstring); a green run here says nothing about `strict`, which drifted the same day. | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: "3.14" | |
| - name: Install PyYAML (the shared context parser imports it) | |
| run: | | |
| # Hash-pinned from the CI toolchain lock, like every other scanner install (ADR 0034 section 3). | |
| python -m pip install --require-hashes -r ci/locks/ci-scanners.lock | |
| - name: Reconcile the required-contexts file against branch protection | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: python scripts/ci/check_required_contexts_drift.py --repo "$GITHUB_REPOSITORY" |