From bd861cf5a2ccf033bc08907bbf7a91a451e5e466 Mon Sep 17 00:00:00 2001 From: Nils Lehnen <30603423+iderex@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:53:33 +0200 Subject: [PATCH] Separate the fork question for the job that reads the ruleset (#62) The arrival walk in docs/quality-parity.md read the triggers, the branch and type filters and the job conditions. All three are about whether a job starts. One name in the candidate set can start and still fail, because half of what it compares is the ruleset on the platform rather than a file in the checkout, and nothing in the document said so. A reader taking the fork clause from it found two upload-derived names there and not this one. The section now names contexts.yml as the only workflow whose own run block asks the platform anything, quotes the step that stops the job when the fetch fails rather than letting it compare against an empty list, and separates the two things a run from a fork brings. The read-only token is measured: on the head of the Dependabot pull request #135 the job reported success and the fetch printed the count it read. The other thing, a token issued against a different repository, is not measured, and the text says so rather than reasoning from the first result to the second. The means is prose in the document the issue names as the place its results go, which carries commands and their output the way the rest of that file does and needs no apparatus to be maintained beside it. Nothing here closes the issue. No pull request from a fork has been opened on this board, and the first clause of the done-condition compares against a required set that is empty today. Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com> --- docs/quality-parity.md | 72 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/docs/quality-parity.md b/docs/quality-parity.md index b47758f..469ee17 100644 --- a/docs/quality-parity.md +++ b/docs/quality-parity.md @@ -353,6 +353,75 @@ reader can take from them. None of them says anything about what a job does once it has started, so a context that arrives having done nothing is a different question and is not answered here. +### One job can start and still be unable to do its work + +The readings above stop at the moment a job begins. One job here has a second +failure after that moment, because half of what it compares is a live setting +on the platform rather than a file in the checkout, so it has to ask. No other +workflow in this tree asks anything: + +``` +git grep -n 'gh api' origin/main -- .github/workflows/ +origin/main:.github/workflows/contexts.yml:93: if ! gh api "repos/${REPOSITORY}/rules/branches/${DEFAULT_BRANCH}" \ +``` + +That reads the run blocks these files carry, so it says which workflow asks the +platform a question in a script this repository writes. What an action reaches +once it has started is not readable from here and the command says nothing +about it. + +The job reports as `required contexts`, which the section above keeps and puts +in the required set. What it does when the answer does not arrive is written +into the step rather than left to the shell: + +``` +git show origin/main:.github/workflows/contexts.yml | sed -n '92,100p' + set -euo pipefail + if ! gh api "repos/${REPOSITORY}/rules/branches/${DEFAULT_BRANCH}" \ + --jq '.[] | select(.type=="required_status_checks") + | .parameters.required_status_checks[].context' > required.txt; then + echo "::error::The ruleset on ${DEFAULT_BRANCH} could not be read, so this run could not judge whether the gate and the tree agree. That is not the same as them agreeing." + exit 1 + fi + echo "the ruleset on ${DEFAULT_BRANCH} requires $(wc -l < required.txt) context(s):" + cat required.txt +``` + +So this route ends in a red context carrying its reason rather than in a +context that never arrives, which is the better of the two failures and is +still a pull request held open by something other than the change on it. An +empty answer is not that failure. The count is printed and the run carries on, +which is the state this board is in today. + +One of the two things a fork run brings is measurable here already. A +Dependabot pull request runs with a read-only token, which is what the upload +condition quoted earlier in this section names it for, and #135 is one. On its +head the job ran and the fetch answered: + +``` +gh api repos/Flowfin/lab/commits/d18d040/check-runs?per_page=100 \ + --jq '.check_runs[] | select(.name=="required contexts") + | "\(.name): \(.conclusion)"' +required contexts: success + +gh run view 31929377870 --log \ + | sed -n 's/.*\(the ruleset on main requires .*\)/\1/p' +the ruleset on main requires 0 context(s): +``` + +A read-only token reads this board's ruleset, so that is not what would stop a +run from a fork. What is left is the other thing, which is that the token is +issued against a different repository. `github.repository` names this board on +a pull request from a fork, so the fetch asks about this board whichever side +the branch sits on, and whether a token issued that way may read this board's +ruleset is platform behaviour that nothing in this tree states and nothing +above measures. + +`required contexts` therefore belongs in the fork clause of #62, and it is +there for a different reason from the two names already in it. Those two are +created by an upload and turn on a write scope. This one is a job that always +starts, and what is open is whether the answer it needs arrives. + ### What this section does not settle No pull request from a fork has been opened here. The condition above has two @@ -360,7 +429,8 @@ arms and only the second was walked: the branch measured above is in this repository, and a read-only token is what the two arms have in common rather than what makes them one route. `CodeQL` did arrive on that pull request, so nothing here says whether it arrives from a fork, and the fork clause of #62 is -open for it. +open for it. It is open for `required contexts` as well, for the reason the +subsection above gives rather than for this one. The required set is empty, so nothing above is a report of a required context that failed to arrive. Every sentence here is about which names a set could