fix(quality): enable-axe went GREEN having measured nothing; the needs-deletion suspicion was a cancelled run - #151
Open
rubenvdlinde wants to merge 9 commits into
Open
Conversation
Temporary harness. Reproduces quality.yml's hydra-gates topology exactly (needs: [playwright] + if: inputs.enable-hydra-gates && !cancelled()) and runs it three times in one run: producer succeeded, failed, skipped. Also measures the candidate always()-based guard side by side.
…g the other 60 gates
… (openbuild's config)
…ded in quality.yml
…e producer fails rather than skips
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was suspected, and what the measurement says
ConductionNL/openregister#2185 showedquality / Hydra Gatestwice in its check list: onceskipping(0s) and oncefailwith 0 seconds duration. A zero-duration "fail" is not a verdict, and #148 had just wiredneeds: [playwright]into that job — so the obvious suspicion was thatneeds:had deleted the job and!cancelled()was not enough to stop it.It had not, and it is. Both halves of the observation are explained without the guard being at fault:
fail, 0s — job91986924166, run30907440099cancelled. A push run superseded by concurrency.gh pr checksrenders CANCELLED as "fail"; a cancelled job carries no verdict.skipping, 0s — job91988619080, run30907444439skipped, and correct: openregister's caller never setsenable-hydra-gates, which defaults tofalse. Playwright in that same run succeeded (3m45s), so theneeds:edge was satisfied and cannot be the cause.The guard, measured in all three producer states
Run
30909727531reproduced this exactneeds:/if:pair three times in one run and drove the producer into each state. The shipped guard and thealways()alternative behaved identically and correctly:inputs.enable-hydra-gates && !cancelled()always() && inputs.enable-hydra-gatesneeds.playwright.result=success)needs.playwright.result=failure)needs.playwright.result=skipped)Confirmed a fourth time end-to-end on the real
quality.yml(run30910234160): Playwright skipped, Hydra Gates ran.!cancelled()is sufficient,always()is not needed, and the comment above the guard now records this so nobody re-derives it. No change to the guard.What the measurement DID find
Keeping the job alive when Playwright is skipped or crashed is right for the other sixty gates — and it is exactly what made gate-33 dangerous. With
enable-axe: true, no artifact could arrive, so:SKIPPED,::notice,Measured, not argued — run
30910234160,Hydra GatesSUCCESS:A caller that explicitly opted into axe enforcement got a green tick over zero accessibility measurement. The absence of the check was indistinguishable from the check succeeding — the shape this file exists to eliminate. The same held for a report that arrived and was rejected by #148's provenance check: deleted, gate-33 skipped, green.
The fix
enable-axe: trueis an opt-in to enforcement, not to a best effort. No usable report is now a failure with a named reason, quoting the producer's resolved state. Not enforcing axe isenable-axe: false— one line in a caller, visible in a diff, attributable to a person.The verdict is recorded at the check and raised at the end of the job, and that detail was itself measured. A plain
exit 1at the check (run30910345294) aborted the job beforeRun the Hydra gates, so a missing axe artifact took the other sixty gates down with it. A gate that suppresses sixty other gates to report itself is its own kind of outage. The gates now run and print in full, then the job fails.Also: the Quality Report gained a
Hydra gatesrow. The gates were inreport'sneeds:and in its failure gate, so a red gates job did red the report — but the artefact people actually read never mentioned sixty gates in any state, which read identically to a repo that has none.Proof
Hydra Gatesquality.yml@main, gates on, axe on, producer off3091023416030910908474Fail when axe was requested but never measured;Run the Hydra gatesstillsuccessand reported in fullenable-axe: false30911069404The arms differ only in which
quality.ymlthey call — same caller, same inputs, one A/B arm per run so the two could not collide on theresult-hydra-gatesartifact name and let the collision, rather than the change, decide the verdict.Positive control on the validator itself, run directly against two fixtures: a well-formed report exits 0 (so
unmeasuredis never written and the new terminal step is skipped — the green path is genuinely still green),{}exits 1.Blast radius
enable-hydra-gatesis set in exactly one repo fleet-wide (openbuild), and no repo anywhere setsenable-axe: true. The no-regression arm reproduces openbuild's live configuration and stays green. Nothing that passes today turns red.The temporary measurement harnesses were removed in the last two commits; their run IDs are recorded in
quality.ymlitself.The producer-failed state, and the one thing not measured end to end
The guard was measured directly against a genuinely failing producer (run
30909727531,needs.playwright.result=failure, gates RAN). The fix was not, and the attempt is recorded rather than hidden: run30911499894turnedenable-playwrighton to force a failing producer, but this repo is not a Nextcloud app, so the security jobs failed first andplaywright's ownneeds.security.result != 'failure'guard turned it back into a skip. The gates job failed correctly there regardless — a second independent absent-report failure — because this step branches on whether a usable report exists, not on how the producer resolved. The producer's state appears only in the error text, which readresolved 'skipped'as expected.