gate-28 fails any PR that touches no lib/**/*.php, in any repo that has a lib/ and a licence in composer.json. Hit on ConductionNL/hermiq#159, a frontend + spec + docs change.
The message
[gate-28] license-triangle: SKIPPED (structural) — lib/ exists and composer.json
declares license=EUPL-1.2, but 0 in-scope lib/**/*.php file carried an @license
or SPDX-License-Identifier declaration, so NOTHING was compared.
...
[hydra-gates] RESULT: ALL GATES PASSED — EXCEPT GATES 28, WHICH DID NOT RUN.
With hydra-gates-require-full-coverage set, that skip is a job failure.
The mechanism
The two halves disagree about what "in scope" means:
- Scope comes from the DIFF.
check_license_triangle.py::main() reads files = argv[2:] — the runner passes the changed-file list (ADR-020, correctly).
- "Subject matter exists" is judged from the REPO —
lib/ is present and composer.json declares a licence.
So for a frontend-only diff the gate is handed zero PHP files, compares nothing, and reports structural — "nothing produced its input". But nothing was supposed to: the PR contains no PHP. That is the definition of NOT APPLICABLE, which the runner explicitly excludes from the coverage verdict.
Verified this is not a repo defect: all 190 of hermiq's lib/**/*.php files carry a licence tag. The gate would pass on any diff that included one.
Why it matters
Every frontend-only PR in every PHP repo in the fleet is blocked by a gate reporting on a subject the PR does not touch. That trains authors to merge over a red Hydra Gates job, which is precisely the habit the coverage requirement exists to prevent.
Suggested fix
Decide applicability from the SAME input as scope: if the changed-file list contains no lib/**/*.php, the gate is NOT APPLICABLE, not structural. structural should mean "PHP files were in scope and none declared anything" — which is a real finding worth failing on.
Same family as #169 (gates 4/24/33 not reporting): a gate that did not run is correctly not treated as a pass, but the applicability test has to be honest about what the PR actually contains.
gate-28 fails any PR that touches no
lib/**/*.php, in any repo that has alib/and a licence incomposer.json. Hit on ConductionNL/hermiq#159, a frontend + spec + docs change.The message
With
hydra-gates-require-full-coverageset, that skip is a job failure.The mechanism
The two halves disagree about what "in scope" means:
check_license_triangle.py::main()readsfiles = argv[2:]— the runner passes the changed-file list (ADR-020, correctly).lib/is present andcomposer.jsondeclares a licence.So for a frontend-only diff the gate is handed zero PHP files, compares nothing, and reports
structural— "nothing produced its input". But nothing was supposed to: the PR contains no PHP. That is the definition of NOT APPLICABLE, which the runner explicitly excludes from the coverage verdict.Verified this is not a repo defect: all 190 of hermiq's
lib/**/*.phpfiles carry a licence tag. The gate would pass on any diff that included one.Why it matters
Every frontend-only PR in every PHP repo in the fleet is blocked by a gate reporting on a subject the PR does not touch. That trains authors to merge over a red Hydra Gates job, which is precisely the habit the coverage requirement exists to prevent.
Suggested fix
Decide applicability from the SAME input as scope: if the changed-file list contains no
lib/**/*.php, the gate is NOT APPLICABLE, notstructural.structuralshould mean "PHP files were in scope and none declared anything" — which is a real finding worth failing on.Same family as #169 (gates 4/24/33 not reporting): a gate that did not run is correctly not treated as a pass, but the applicability test has to be honest about what the PR actually contains.