Skip to content

gates 19/25/26 are still diff-scoped on every pull_request — HYDRA_GATE_BASE_REF leaks past the SCOPE_TO_DIFF guard, so #378 does not reach the event that gates merges #416

Description

@rubenvdlinde

Summary

Since #378 made whole-tree the default, gates 19, 25 and 26 are still silently diff-scoped on every pull_request run — i.e. on exactly the event that gates a merge. They report their whole-tree backlog only on push, after the merge, where it can no longer block anything.

The cause is not the scope flag. It is that those three helpers receive the base ref through the environment, and HYDRA_GATE_BASE_REF is present in the environment on pull_request and absent on push.

Isolated control — same tree, same base commit, only the delivery channel differs

Local run of bin/hydra-gates against docudesk, base f1931874 in both arms, SCOPE-MODE: full in both:

gate HYDRA_GATE_BASE_REF=f1931874 bin/hydra-gates --app-dir D env -u HYDRA_GATE_BASE_REF bin/hydra-gates --app-dir D --base f1931874
19 e2e-coverage NOT APPLICABLE FAIL — 396 scenario(s)
25 contract-coverage NOT APPLICABLE PASS (it actually ran)
26 visual-coverage NOT APPLICABLE FAIL — 6 new page component(s)
COVERAGE: 56 of 65 COVERAGE: 58 of 65

Both arms print the same resolved base — only the source label changes (($HYDRA_GATE_BASE_REF) vs (--base)). Nothing else about the invocation differs.

The same split reproduces in CI as a push-vs-PR difference

repo event base line in the log gate-19 COVERAGE
hermiq push (2 changed files) Delta base: 51e128e… (github.event.before (push)) FAIL — 491 60 of 65
hermiq pull_request (4 changed files) Delta base: origin/development ($HYDRA_GATE_BASE_REF) NOT APPLICABLE 58 of 65
docudesk push (0 changed files) Delta base: 18e17b1f… (github.event.before (push)) FAIL — 396 62 of 65
docudesk pull_request (1 changed file) Delta base: origin/development ($HYDRA_GATE_BASE_REF) NOT APPLICABLE 58 of 65

On push the wrapper resolves the base from github.event.before into a shell variable and forwards it as an argument; the env var is not set, so the helper sees no base and sweeps the tree. On pull_request the caller exports HYDRA_GATE_BASE_REF, and the helper picks it up regardless of SCOPE_TO_DIFF.

Why the existing guard does not catch it

run-hydra-gates.sh gate-19 already knows about this class — its comment cites #242:

SCOPE ONLY WHEN THE CALLER ASKED FOR IT (#242). BASE_REF was passed unconditionally, so an UNSCOPED run — the mode a fleet audit uses — was silently narrowed to the diff against origin/development, came back empty, and the helper printed PASS over a repo it never opened. Measured on openconnector: 5 findings scoped, 412 over the full tree.

and the code correctly gates the explicit pass:

if [ "${SCOPE_TO_DIFF}" = "1" ]; then
    HYDRA_GATE_BASE_REF="${BASE_REF}" python3 …/check_e2e_coverage.py . >> "${_e2e_log}" 2>&1
else
    python3 …/check_e2e_coverage.py . >> "${_e2e_log}" 2>&1
fi

The else branch does not unset the variable. An inherited HYDRA_GATE_BASE_REF walks straight past the if into the child process, and #242 is re-opened by the environment rather than by the argument.

⚠️ This also silently sabotages local reproduction: my first attempt at reproducing docudesk's gate-19 used HYDRA_GATE_BASE_REF=… and got NOT APPLICABLE on a tree I already knew carried 396. That reads exactly like a clean repo.

Impact

#378's stated purpose is that a tightened gate should force the inherited debt to be fixed. For the three coverage gates that does not happen where it would bite:

  • docudesk: 402 findings (396 + 6) invisible on every PR.
  • hermiq: 502 findings (491 + 5 + 6) invisible on every PR.

And the two events produce numbers that look like progress. docudesk development push reads RESULT: 5 GATE(S) FAILED; a one-file PR against it reads RESULT: 2 GATE(S) FAILED. Nothing was fixed in between. Anyone quoting a PR gate count as a repo state — or comparing one to a push count — records a three-gate improvement that does not exist.

Suggested fix

Unset the variable in the else branch for each of the three helpers, so the environment cannot re-introduce the scoping the flag declined:

else
    env -u HYDRA_GATE_BASE_REF python3 …/check_e2e_coverage.py . >> "${_e2e_log}" 2>&1
fi

⚠️ Expect the number to jump on the next PR in every repo, which is the point of #378 — but it should land deliberately rather than as a surprise on someone's unrelated one-line change.

Worth adding to the gate-acceptance fixtures as a two-arm case: same base, delivered two ways, must produce the same verdict. That property is what failed here, and it is cheap to pin.

Measured on ConductionNL/.github@923f57d locally and against CI runs using 5e73e640 and e3da4103; the split is present in all three.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions