You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
hydra-gates/quality-config/phpstan-base.neon carries these under ignoreErrors:
- '#unknown class OCA\\OpenRegister\\#'
- '#OCA\\OpenRegister\\[a-zA-Z\\]+.*not found#'
- '#on an unknown class OCA\\OpenRegister\\#'
- '#has invalid return type OCA\\OpenRegister\\#'
- '#has invalid type OCA\\OpenRegister\\#'
- '#implements unknown interface OCA\\OpenRegister\\#'
Every leaf app includes this base. So a green phpstan has never been evidence
that any OpenRegister type resolves, in any app. If a leaf app typehints a
contract that does not exist, has been renamed, or has drifted, phpstan reports
nothing and exits 0.
Found while cleaning up the register-slug resolver adoption on buildiq, shillinq
and hermiq. shillinq's phpstan happened to be looking at real files through scanDirectories; buildiq's and hermiq's never were, and all three were equally
green.
The root cause underneath it
conduction/hydra-gates ships hydra-gates/contracts/ but its composer.json
declares no autoload section at all. So nothing in that directory is
autoloadable by a consumer, which is why every app needs some private mechanism
to see the contracts: an ignoreErrors entry, a scanDirectories entry, a
psalm referencedClass suppression, a local stub copy, or a hand-written require_once in a test bootstrap. Five mechanisms across the fleet, for one
package that could declare the mapping once.
Measured on buildiq: deleting the referencedClass suppression for ObjectEntityInterface, which the package has shipped since long before v1.17.0, makes psalm exit 2. A tagged release was never what made these
resolvable.
Two remedies, one of which is already in use
Give the package an autoload entry for OCA\OpenRegister\Contract\.
This is the one that would let the fleet-wide phpstan ignores go. It needs
care: hydra-gates claims OCA\OpenRegister\Contract\ as a RUNTIME psr-4 prefix, so any app vendor can define another app contract #531 removed a runtime psr-4 prefix for this exact
namespace because the prefix is longer than openregister's own, so a vendored
copy in any app defined the contract for the whole process. An
analysis-only autoload entry is not obviously the same defect, but it is
adjacent to it and deserves a real decision rather than a quick patch.
Remedy 2 does nothing for phpstan. The ignoreErrors block above is still
there, still fleet-wide, and still means phpstan green says nothing about
OpenRegister types.
Why this is worth acting on rather than noting
A referencedClass suppression makes every value flowing through the contract mixed, so the analyser stops checking the branch the contract exists to force.
On decidesk, swapping suppressions for stubs surfaced a real nullable that had
shipped. The phpstan ignores are the same shape at fleet scale, and nothing has
looked behind them yet.
What was measured
hydra-gates/quality-config/phpstan-base.neoncarries these underignoreErrors:Every leaf app includes this base. So a green phpstan has never been evidence
that any OpenRegister type resolves, in any app. If a leaf app typehints a
contract that does not exist, has been renamed, or has drifted, phpstan reports
nothing and exits 0.
Found while cleaning up the register-slug resolver adoption on buildiq, shillinq
and hermiq. shillinq's phpstan happened to be looking at real files through
scanDirectories; buildiq's and hermiq's never were, and all three were equallygreen.
The root cause underneath it
conduction/hydra-gatesshipshydra-gates/contracts/but itscomposer.jsondeclares no
autoloadsection at all. So nothing in that directory isautoloadable by a consumer, which is why every app needs some private mechanism
to see the contracts: an
ignoreErrorsentry, ascanDirectoriesentry, apsalm
referencedClasssuppression, a local stub copy, or a hand-writtenrequire_oncein a test bootstrap. Five mechanisms across the fleet, for onepackage that could declare the mapping once.
Measured on buildiq: deleting the
referencedClasssuppression forObjectEntityInterface, which the package has shipped since long beforev1.17.0, makes psalm exit 2. A tagged release was never what made theseresolvable.
Two remedies, one of which is already in use
Give the package an
autoloadentry forOCA\OpenRegister\Contract\.This is the one that would let the fleet-wide phpstan ignores go. It needs
care: hydra-gates claims OCA\OpenRegister\Contract\ as a RUNTIME psr-4 prefix, so any app vendor can define another app contract #531 removed a runtime psr-4 prefix for this exact
namespace because the prefix is longer than openregister's own, so a vendored
copy in any app defined the contract for the whole process. An
analysis-only autoload entry is not obviously the same defect, but it is
adjacent to it and deserves a real decision rather than a quick patch.
List the contract files as psalm
<stubs>. Available today, no packagechange. decidesk and pipelinq already do it. buildiq, shillinq and hermiq
have now adopted it too, in chore(deps-dev): take hydra-gates 1.18.0 and drop the tag-gap workarounds buildiq#741,
chore(deps-dev): take hydra-gates 1.18.0 and drop the copied contracts shillinq#1567 and chore(deps-dev): take hydra-gates 1.18.0 and drop the tag-gap workarounds hermiq#841, which removed ten
referencedClasssuppressions between them.Remedy 2 does nothing for phpstan. The
ignoreErrorsblock above is stillthere, still fleet-wide, and still means phpstan green says nothing about
OpenRegister types.
Why this is worth acting on rather than noting
A
referencedClasssuppression makes every value flowing through the contractmixed, so the analyser stops checking the branch the contract exists to force.On decidesk, swapping suppressions for stubs surfaced a real nullable that had
shipped. The phpstan ignores are the same shape at fleet scale, and nothing has
looked behind them yet.
🤖 Generated with Claude Code