ci(quality): move hydra-gates-ref to v1.5.0 — restore the gates - #287
Closed
rubenvdlinde wants to merge 1 commit into
Closed
ci(quality): move hydra-gates-ref to v1.5.0 — restore the gates#287rubenvdlinde wants to merge 1 commit into
rubenvdlinde wants to merge 1 commit into
Conversation
…onger satisfies the floating workflow
`quality.yml` is consumed `@main` and executes three scripts BY PATH inside the
PINNED hydra-gates package:
scripts/lib/check_spec_anchors.py
scripts/lib/check_form_labels.py
scripts/lib/check_license_triangle.py
Verified by recursive tree listing per ref: v1.0.1, v1.2.0, v1.3.0 and v1.4.0
each contain 0 of 3; only v1.5.0 and main contain all three. So every consumer
below v1.5.0 fails with exit 1, and the workflow says so itself:
::error::hydra-gates-ref does not contain: ...
::error::This workflow floats on @main and executes those paths by name
inside the PINNED package.
This is NOT a code-quality finding about this repository — it is the
floating-caller / pinned-callee split, third occurrence today.
v1.5.0 also carries large genuine gate improvements, measured across the same
22 fleet trees with each version running its own code:
gate-46 spec-anchor-existence 2,228 -> 918
gate-40 form-label-association 1,211 -> 517
gate-9 semantic-auth 45 -> 11
Every relaxed predicate in it was mutation-checked both ways: always-true and
always-false must each fail.
Diff is exactly one line.
Contributor
Quality Report — ConductionNL/scholiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ✅ | ✅ 750/750 | |||
| PHPUnit | ❌ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-06 07:29 UTC
Download the full PDF report from the workflow artifacts.
Contributor
Author
|
Superseded by #285, which removes The product owner's directive is to stop pinning the package per repo so every repo always runs the newest gates. Moving the pin to v1.5.0 would leave the same silent expiry date in place — the shared workflow now declares the default ( |
rubenvdlinde
added a commit
that referenced
this pull request
Aug 6, 2026
…ppHost (gate-64 / ADR-040) (#288) * fix(apphost): register OpenRegister's autoloader before referencing AppHost Nextcloud registers apps in sorted order: OC_App::getEnabledApps() does sort($apps) and Coordinator::registerApps() walks that list calling OC_App::registerAutoloading($appId, $path) and then $app->register() for one app at a time, so every app registers before the PSR-4 prefix of every alphabetically-later app exists. `scholiq` sorts after `openregister`, so OCA\OpenRegister\ happens to be autoloadable here today — by alphabet, not by design. Scholiq depends on that accident more sharply than most: its Bootstrap::register() call is UNGUARDED, so the moment the ordering stops holding the resulting \Error aborts the WHOLE of Application::register(). Coordinator catches it, logs an 'emergency' and continues, leaving Scholiq enabled and serving with ServiceOverrideRegistrar and EventListenerWiring silently never run. Fix: register OpenRegister's prefix ourselves first. registerAutoloading() touches only the autoloader and is idempotent, so on the current ordering this costs nothing. IAppManager::loadApp() is deliberately NOT used: it marks OpenRegister loaded and calls Coordinator::bootApp(), booting it before its own register() has run. Caught by hydra gate-64 (apphost-autoload-prelude), ADR-040. Unblocks the gate-64 failure on the hydra-gates v1.5.0 bump PR (#287). * fix(apphost): make the prelude branch-free and declare OC_App to psalm Two CI findings on the prelude, both real: 1. psalm UndefinedClass on \OC_App. It is Nextcloud's server-private legacy bootstrap class, absent from nextcloud/ocp, and there is no OCP interface for registering another app's autoloader. Declared as a suppressed referencedClass in psalm.xml, the same way doriath declares it. 2. The coverage ratchet. `return true` after the call plus `return false` in the catch gave the method a branch that NO environment can exercise both sides of — whichever runs, the other is dead in that run — so the class could never reach full line coverage. No caller ever consumed the return value either: what callers depend on is the class_exists() guard that follows the call. The method is now void with a single statement in the try and a comment-only catch, so every executable line runs in every environment. The tests now assert the two things that are actually observable: that control returns to the caller at all (a Throwable escaping would fail the test, and in production would abort the whole register()), and that a second call does not stack another autoloader. phpmd StaticAccess on the new composition-root call is documented on the calling method rather than baselined. * docs(spec): state the prelude invariant as prose, not as excluded scenarios The two scenarios added for the ADR-040 prelude each carried an '@e2e exclude'. An exclusion is not evidence, and neither behaviour is reachable from a browser or an HTTP client: both live in the app-registration phase, which completes before the first request is dispatched, and the absent-OpenRegister path cannot be set up on an instance that needs OpenRegister to serve the app at all. Stated in the requirement prose instead, naming the unit test that does assert them (tests/Unit/AppInfo/OpenRegisterAutoloaderTest.php). No scenario is declared, so none is excluded. * test: cover the prelude's degraded path, which no instance could reach The coverage ratchet was right and the code was wrong. Clover for scholiq shows it exactly: line 100 (the registerAutoloading call) count=2, line 101 (the catch) count=0. The catch was never entered — because every instance this suite runs on HAS OpenRegister installed, so getAppPath() never throws. The never-rethrow branch, which is the entire reason this class exists, had never once been executed by a test. register() now takes an optional app id. Production callers pass nothing and get 'openregister'; the new test passes an id that cannot resolve, so getAppPath() throws and the catch runs. The literal stays AT the registerAutoloading call site rather than becoming a signature default, so it remains visible to a reader and to hydra gate-64, which reads that call's arguments. The new test asserts something real rather than merely not throwing: a prelude whose app cannot be resolved must leave spl_autoload_functions() untouched.
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.
quality.ymlis consumed@mainand executes three scripts by path inside the pinned hydra-gates package:Verified by recursive tree listing per ref:
So every consumer below v1.5.0 fails with
exit 1, and the workflow says so itself:This is not a code-quality finding about this repository. It is the floating-caller / pinned-callee split — the third occurrence today, after the
require-full-coveragedefault change reaching old runners and#168executingaxe-run.cjsby path.What v1.5.0 also brings
Measured across the same 22 fleet trees, each version running its own code (no CI output read, so a split fleet cannot skew it):
Every relaxed predicate was mutation-checked both ways — always-true and always-false must each fail — so the reduction is precision, not blindness.
It also fixes the
SCOPE WAS EMPTYreporting bug (an unanchoredgrep -q "0 changed file(s)", which10 changed file(s)also matched), the hardcoded/tmp/hydra-gate-*.logcollision that produced cross-repo false findings, and a gate-28 NUL-byte hole that could yield a false green.Scope
Exactly one line. No other change.
developmentthe gate diffs againstorigin/development— itself — so scope is 0 files and v1.5.0 exits 99. A scoping fix (againstgithub.event.before) is in progress. That is a true statement replacing a vacuous pass, and strictly better than failing because a script is missing.