fix(bulk): one resolution implementation, because #2820 survived its own fix - #2862
Merged
rubenvdlinde merged 2 commits intoAug 26, 2026
Merged
Conversation
…own fix Deploying #2858 and #2860 took the objects API from 2 of 16 registers working to 16 of 16. The archived-corpus import still failed on its first project: POST /api/bulk/19/9475/save 404 {"error": "Register not found: '19'"} for the same register `GET /api/objects/19/9476` had just served. `BulkController` held its **own private copy** of `resolveRegisterSchemaIds()`, identical to `ObjectsController`'s until I fixed one of them — after which they were not identical, and the bug lived on in the copy nobody edited. Fixing the second copy would have left the same trap set for the third, so the logic now has one home: `ResolvesRegisterAndSchemaTrait`, used by both. Two behaviours it must keep, both load-bearing and both learned the hard way: - **`clearCurrents()` first.** `ObjectService` is shared within a request, so a schema ref left pending by an earlier caller is otherwise re-resolved inside whichever register THIS call names. - **Report a schema failure as a schema failure.** `setRegister()` assigns `currentRegister` before re-resolving a pending ref, so a changed entity means the register resolved and the throw came from the schema side. Without that, the endpoint blames a register that demonstrably exists — which is what made #2820 cost a day: every reasonable first move (check the row, the magic tables, the organisation filter, run the query in psql) investigates the wrong thing. `BulkController` keeps its re-anchor on the resolved numeric ids, because its downstream handlers read the service's current register/schema rather than the returned array. That is behaviour-preserving, not incidental. ## The test is the point `RegisterSchemaResolutionParityTest` asserts both controllers route through the trait, and that **no controller file contains `clearCurrents(`** — the tell of a pasted copy. A future third controller that duplicates the helper fails here. Mutation-checked: removing the trait from `BulkController` fails with *"BulkController resolves register/schema without the shared trait — that is how openregister#2820 survived its own fix"*. That guard matters more than this fix. The defect was never the logic; it was having two of it. phpcs, phpstan clean on all three changed lib files; 8 tests across the parity and attribution suites. Refs #2820
Contributor
Quality Report — ConductionNL/openregister @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 175/175 | |||
| npm | ✅ | ✅ 545/545 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-25 22:35 UTC
Download the full PDF report from the workflow artifacts.
gate-1 spdx-headers failed with '0 missing @license, 1 missing @copyright' — the new trait carried the SPDX-FileCopyrightText line and the @license tag but not the @copyright PHPDoc tag the gate checks for. They are different things and only one of them was present.
Contributor
Quality Report — ConductionNL/openregister @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 175/175 | |||
| npm | ✅ | ✅ 545/545 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-08-26 08:07 UTC
Download the full PDF report from the workflow artifacts.
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.
fix(bulk): one resolution implementation, because #2820 survived its own fix
Deploying #2858 and #2860 took the objects API from 2 of 16 registers working to
16 of 16. The archived-corpus import still failed on its first project:
for the same register
GET /api/objects/19/9476had just served.BulkControllerheld its own private copy of
resolveRegisterSchemaIds(), identical toObjectsController's until I fixed one of them — after which they were notidentical, and the bug lived on in the copy nobody edited.
Fixing the second copy would have left the same trap set for the third, so the
logic now has one home:
ResolvesRegisterAndSchemaTrait, used by both.Two behaviours it must keep, both load-bearing and both learned the hard way:
clearCurrents()first.ObjectServiceis shared within a request, so aschema ref left pending by an earlier caller is otherwise re-resolved inside
whichever register THIS call names.
setRegister()assignscurrentRegisterbefore re-resolving a pending ref, so a changed entity meansthe register resolved and the throw came from the schema side. Without that,
the endpoint blames a register that demonstrably exists — which is what made
Objects API answers 404 'Register not found' for most registers, and RegisterMapper::find() is never reached #2820 cost a day: every reasonable first move (check the row, the magic
tables, the organisation filter, run the query in psql) investigates the wrong
thing.
BulkControllerkeeps its re-anchor on the resolved numeric ids, because itsdownstream handlers read the service's current register/schema rather than the
returned array. That is behaviour-preserving, not incidental.
The test is the point
RegisterSchemaResolutionParityTestasserts both controllers route through thetrait, and that no controller file contains
clearCurrents(— the tell of apasted copy. A future third controller that duplicates the helper fails here.
Mutation-checked: removing the trait from
BulkControllerfails with"BulkController resolves register/schema without the shared trait — that is how
openregister#2820 survived its own fix".
That guard matters more than this fix. The defect was never the logic; it was
having two of it.
phpcs, phpstan clean on all three changed lib files; 8 tests across the parity
and attribution suites.
Refs #2820