docs(ci-cd): how an app depends on OpenRegister (ADR-083 + ADR-084) - #466
Merged
Conversation
added 2 commits
August 15, 2026 15:56
ADR-084 landed with the ADR itself in hydra/openspec and the reasoning inside the checker, but nothing in the CI/CD page an app developer actually reads. So the rule — type-hint OCA\OpenRegister\Contract\ObjectServiceInterface, never the concrete class — was enforced by a gate and written down nowhere. Three additions: * A section on consuming OpenRegister: the type-hint, the composition-root alias, why Nextcloud's autowiring needs it stated, and the trap that is easy to get wrong — keep the CONCRETE import where the concrete NAME is still used. `ObjectService::class` does not require the class to exist, so dropping that import silently resolves the key to the app's own namespace and looks up something nobody registered; `instanceof` just becomes permanently false. * "Two distribution paths in one package". hydra-gates now ships runtime interfaces as well as gate scripts, and they reach an app differently: the scripts are fetched from `.github@main` and are live on merge, the contracts travel by composer and need a TAG plus a lock bump. Confusing the two cost a release here — openregister bumped its lock specifically to make gate-67 enforce, and the gate still reported NOT APPLICABLE, because the Hydra Gates job never runs `composer install` and so has no vendor/ to read. * The package table row, which claimed hydra-gates contains the gates and quality-config. It now also contains the contract. The two lessons are stated where they will be read: a gate that has only ever skipped has not been shown to work, and NOT APPLICABLE is deliberately not a pass — the wording was right and the gate was still useless.
The previous commit documented what to type-hint but not when to inject at all, which left the larger half of the rule in an ADR nobody reads before their build goes red. ADR-083 and ADR-084 are now one section, because they are one decision in two parts: rule 1 says inject the dependency, and it is unenforceable until there is an interface to inject. Adds the four rules with the reasoning that is easy to get wrong: * the optional-capability EXCEPTION, and that a guard has three valid spellings — ask the app manager, ask the autoloader, or try-and-degrade. A catch that RETHROWS is not a guard. This is the part a reader is most likely to get wrong, and the part that turns a clean "not installed" message into a 500. * why `extends`/`implements` against an OpenRegister class is categorically different from injection: a class header is fatal at autoload and takes down the route that would have explained the problem. * the start-screen rule, and the version-floor rule (detect and inform; never install — `OC\Installer` is private API and bypasses admin consent). And it records that the rule was too broad when first written and the gate enforced it faithfully: 1263 → 1010 → 883 → 441 findings, the 822-site difference being code that was already correct. A rule derived from one observed idiom will mistake every other correct idiom for debt, at fleet scale. Also names the cost that motivated the ADR rather than asserting readability: gate-7 reported 50 findings on pipelinq, every one a correctly-delegated endpoint, because a string literal is invisible to it — and a human review of the same code the same day drew the opposite conclusion about who enforced authorisation, and wrote it down.
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.
The gap
Both ADRs shipped with the ADR in
hydra/openspecand the reasoning inside thecheckers — and nothing in the CI/CD page an app developer actually reads. The
rules were enforced by two gates and written down nowhere, so the first time a
developer meets them is a red build.
What the page now says
ADR-083 — the four rules, enforced by gate-66. Inject an unconditional
dependency; never
extends/implementsan OpenRegister class; keep the defaultroute core-only; check the version floor but never install.
With the parts that are easy to get wrong:
spellings — ask the app manager, ask the autoloader, or try-and-degrade. A
catchthat rethrows is not a guard. Getting this wrong turns a clean"OpenRegister is not installed" message into a 500.
autoload, and takes down the very route that would have explained the problem.
OC\Installeris private API and bypasses admin consent — detect andinform, never install.
ADR-084 — type-hint the contract, gate-67. The interface, the
composition-root alias, why Nextcloud's autowiring needs it stated, and:
"Two distribution paths in one package." hydra-gates now ships runtime
interfaces as well as gate scripts, and they reach an app differently:
.github@maincontracts/vendor/^1.0Confusing the two cost a release: openregister bumped its lock specifically to
make gate-67 enforce, and the gate still said
NOT APPLICABLE, because theHydra Gates job never runs
composer install(fixed in #465).Plus the package table row, which still claimed hydra-gates contains only the
gates and
quality-config/.Lessons recorded where they will be read
NOT APPLICABLEis deliberately not a pass — the wording was right and the gate wasstill useless.
for debt, at fleet scale and with a straight face. ADR-083's count went
1263 → 1010 → 883 → 441; the 822-site difference was already-correct code.
50 findings on pipelinq, every one a correctly-delegated endpoint, because
a string literal is invisible to it — and a human review of the same code the
same day concluded the opposite about who enforced authorisation.
Related
implements— feat(contract): publish ObjectService/ObjectEntity interfaces — one definition instead of eight openregister#2498🤖 Generated with Claude Code