Skip to content

docs(hydra-gates): the contract opt-in must cover the analysers too - #551

Merged
rubenvdlinde merged 1 commit into
mainfrom
docs/contract-optin-covers-analysers
Aug 22, 2026
Merged

docs(hydra-gates): the contract opt-in must cover the analysers too#551
rubenvdlinde merged 1 commit into
mainfrom
docs/contract-optin-covers-analysers

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

The opt-in section added with #547 explained how to make the interface resolvable at runtime and stopped there. That omission is how the gap reached the fleet.

Psalm and PHPStan never run the test bootstrap. They resolve types through the composer autoload map, so the guarded require is invisible to them. If the interface appears in production code — constructor promotion, a parameter type, a ::class fetch in lib/ — every mention becomes an undefined class.

Measured on adoption of v1.9.0:

app errors
decidiq 204 UndefinedClass, all OCA\OpenRegister\Contract\ObjectServiceInterface
stackiq 213, same class

Both went red in CI, and both are fixed by a stub (ConductionNL/decidiq#843, ConductionNL/stackiq#706) — verified 204 → 0 and 213 → 0 locally on the real v1.9.0.

Why a stub and not an autoload entry

The class exists at runtime because OpenRegister supplies it. Putting it back into the autoloader is precisely the defect #531 removed. A stub teaches the analyser the shape without re-creating the shadowing.

Why PHPStan's entry is per-app

openregister owns the real lib/Contract/. Scanning the vendored copy in the shared base would declare the same class twice, in the one repo that must not.


The migration was verified against PHPUnit in both directions and the analysers were never run. The doc now says: check lib/, not just tests/.

The opt-in section told adopters how to make the interface resolvable AT
RUNTIME and stopped there. That is how I missed it: psalm and phpstan never run
the test bootstrap — they resolve types through the composer autoload map — so
the guarded require does nothing for them.

If the interface appears in PRODUCTION code rather than only in tests, removing
the psr-4 prefix turns every mention into an undefined class. Measured on
adoption of v1.9.0: 204 errors on decidiq, 213 on stackiq, all the same class,
and both went red in CI.

Adds the two seams that fix it — psalm `<stubs>`, phpstan `scanDirectories` —
with the reason they are stubs rather than autoload entries: the class exists at
runtime because OpenRegister supplies it, and putting it back into the
autoloader is exactly the defect #531 removed.

Also records why phpstan's entry belongs in the APP's config and not the shared
base: openregister owns the real lib/Contract/, so scanning the vendored copy
there would declare the same class twice.

The migration was verified against PHPUnit in both directions and the analysers
were never run. Check lib/, not just tests/.
@rubenvdlinde
rubenvdlinde merged commit e68cec7 into main Aug 22, 2026
36 checks passed
@rubenvdlinde
rubenvdlinde deleted the docs/contract-optin-covers-analysers branch August 22, 2026 09:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant