Skip to content

fix(bulk): one resolution implementation, because #2820 survived its own fix - #2862

Merged
rubenvdlinde merged 2 commits into
developmentfrom
fix/bulk-resolution-shares-one-implementation
Aug 26, 2026
Merged

fix(bulk): one resolution implementation, because #2820 survived its own fix#2862
rubenvdlinde merged 2 commits into
developmentfrom
fix/bulk-resolution-shares-one-implementation

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

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:

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
    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.

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

…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
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openregister @ f2736dd

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.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openregister @ ad969cb

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.

@rubenvdlinde
rubenvdlinde merged commit 53c54a4 into development Aug 26, 2026
83 checks passed
@rubenvdlinde
rubenvdlinde deleted the fix/bulk-resolution-shares-one-implementation branch August 26, 2026 08:49
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