Repository navigation
fix(rbac): break the MagicMapper ↔ MemberOfResolver construction cycle - #4785
Conversation
openregister#4741 gave ConditionMatcher an autowired MemberOfResolver, whose
constructor took MagicMapper. MagicMapper's constructor builds its RBAC
handler, which resolves ConditionMatcher from the container, so building
MagicMapper re-entered the MagicMapper factory until the stack was exhausted
("Maximum call stack size ... Infinite recursion?"), and `occ app:enable
openregister` failed.
MemberOfResolver now takes the ContainerInterface and fetches MagicMapper on
first read, the same lazy-resolution pattern used for CacheHandler (issue
#1564). RBAC behaviour is unchanged; a failed resolution still falls inside the
existing try/catch and denies.
Tests: constructors updated; regression tests assert that construction never
touches the container and that no constructor parameter is a MagicMapper.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Quality Report — ConductionNL/openregister @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| test-l10n-parity | ✅ | ||||
| format | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 173/173 | |||
| npm | ✅ | ✅ 648/648 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ✅ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ❌ 26878/26879 · 25 skipped | ||||
| Newman | ✅ 714/714 | ||||
| Playwright | ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development. |
||||
| Hydra gates | ❌ |
Quality workflow — 2026-10-22 17:25 UTC
Download the full PDF report from the workflow artifacts.
FlowDryRunService::dryRun (#4753) called ObjectService::find() without a decision, so RecentLensLookupCallSitesTest::testEveryInternalFindHasADecision fails on development. A dry run reads the object to try the flow; nobody opens it, so it runs under WriteCause::asLookup: the audit row stays, the object does not enter the caller's recently-opened list. The test only became visible here because the construction cycle fixed in this PR stopped PHPUnit from running at all. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…emberof-resolver # Conflicts: # lib/Service/Flow/FlowDryRunService.php
Quality Report — ConductionNL/openregister @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| test-l10n-parity | ✅ | ||||
| format | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 173/173 | |||
| npm | ❌ | ✅ 648/648 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ✅ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ❌ cancelled — no verdict | ||||
| Newman | ✅ 714/714 | ||||
| Playwright | ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development. |
||||
| Hydra gates | ❌ |
Quality workflow — 2026-10-22 22:57 UTC
Download the full PDF report from the workflow artifacts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Quality Report — ConductionNL/openregister @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| test-l10n-parity | ✅ | ||||
| format | ❌ | ||||
| check-schema-l10n | ❌ | ||||
| composer | ✅ | ✅ 173/173 | |||
| npm | ❌ | ✅ 648/648 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ✅ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ❌ 27153/27160 · 25 skipped | ||||
| Newman | ✅ 714/714 | ||||
| Playwright | ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development. |
||||
| Hydra gates | ❌ |
Quality workflow — 2026-10-26 12:26 UTC
Download the full PDF report from the workflow artifacts.
MatrixRowConditionsBothPathsTest arrived on development after this branch and built MemberOfResolver with a MagicMapper. The resolver now takes the container and reads the mapper from it on first use, so the harness's container hands the mapper out next to the services it already serves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Quality Report — ConductionNL/openregister @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| test-l10n-parity | ✅ | ||||
| format | ❌ | ||||
| check-schema-l10n | ❌ | ||||
| composer | ✅ | ✅ 173/173 | |||
| npm | ❌ | ✅ 648/648 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ✅ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ❌ 27187/27188 · 25 skipped | ||||
| Newman | ✅ 714/714 | ||||
| Playwright | ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development. |
||||
| Hydra gates | ❌ |
Quality workflow — 2026-10-26 13:34 UTC
Download the full PDF report from the workflow artifacts.
The lazy container->get(MagicMapper::class) sits inside read()'s try, so a container failure denies like a failing search. A test now reaches that branch; moving the get out of the try turns it red. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Quality Report — ConductionNL/openregister @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| test-l10n-parity | ✅ | ||||
| format | ❌ | ||||
| check-schema-l10n | ❌ | ||||
| composer | ✅ | ✅ 173/173 | |||
| npm | ❌ | ✅ 648/648 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ✅ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ❌ 27206/27207 · 25 skipped | ||||
| Newman | ✅ 714/714 | ||||
| Playwright | ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development. |
||||
| Hydra gates | ❌ |
Quality workflow — 2026-10-26 15:13 UTC
Download the full PDF report from the workflow artifacts.
WilcoLouwerse
left a comment
There was a problem hiding this comment.
Verdict: APPROVE (Strict · full)
Posted as COMMENT: GitHub refuses an approval on your own pull request.
MemberOfResolver takes the container and reads MagicMapper on first use, inside read()'s existing try/catch, so constructing it no longer recurses through MagicMapper → ConditionMatcher → MemberOfResolver. Every construction site passes a container, and a container that cannot give the mapper denies like a failing search. Nothing blocks.
✅ Fixed before posting (2)
- Two tests that arrived on
developmentafter this branch (MatrixRowConditionsBothPathsTest, borrowed byMatrixRowsFromAppsBothPathsTest) still built the resolver with a mapper: 7TypeErrors on the previous head. Fixed in 9b120d0. - 🟢 No test reached the branch where
container->get(MagicMapper::class)itself fails.testAContainerThatCannotGiveTheMapperGivesAnEmptySetdoes now, and moving thegetout of the try turns it red. Fixed in 1a06189.
✅ Verified clean
- No other path back to
MagicMapper: a constructor-graph walk from every serviceMagicMapper::initializeHandlers()fetches finds the one known cycle ondevelopmentand none on this head. read()'s fallback is fail-closed ([], the caller is a member of nothing).- The suite on the previous head ran 26,914 tests with no error or failure.
CI on this head
- Not from this PR, inherited from
development:Frontend Check (format)(a spec file),Security (npm)(dompurify, fixed by #4791) andNewman. - PHPUnit was still running when this was posted. On the old head it was canceled at the 45-minute limit while it ran the coverage ratchet's second pass on the merge base, which still has the cycle.
Gates: quality / Hydra Gates was running on this head when this was posted; on development it fails on gates 6, 7 and 49, which #4786 clears.
Reviewed: 5 files, 1a06189.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… reads #4910 made getSearchIndexStatus() read every table's index names in one indexNamesFor() call; the coverage test still stubbed indexesFor(), so the mock answered nothing and tableCount came back 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Why
Since #4741,
occ app:enable openregisterdies with Maximum call stack size … Infinite recursion? in the DI container: theMagicMapperfactory builds its RBAC handler, which resolvesConditionMatcher, which autowiresMemberOfResolver, whose constructor asked forMagicMapperagain. Every open openregister PR fails PHPUnit (stable35) and Newman on it, because the job cannot enable the app.What
MemberOfResolvertakes the container instead ofMagicMapperand fetchesMagicMapperon first read, inside the existing try/catch (a failure still denies). RBAC behaviour is unchanged.MemberOfResolverTest: construction resolves nothing from the container, the constructor has noMagicMapperparameter, and a container that cannot give the mapper admits nobody.MatrixRowConditionsBothPathsTest, which arrived ondevelopmentafter this branch.Tests
MemberOfResolverTestOK (17 tests),ConditionMatcherMemberOfTestOK (8 tests).MagicRbacMemberOfListTestOK (4),MatrixRowConditionsBothPathsTestOK (4),MatrixRowsFromAppsBothPathsTestOK (3), run in a PHP 8.3 container withpdo_sqlite.getout ofread()'s try turns the new container-failure test red.occ app:enableend to end; this PR's PHPUnit and Newman jobs are that check.🤖 Generated with Claude Code