Skip to content

fix(scope): clear the pending schema ref on the save path too - #2803

Merged
rubenvdlinde merged 2 commits into
developmentfrom
fix/pending-schema-ref-leaks-into-save
Aug 22, 2026
Merged

fix(scope): clear the pending schema ref on the save path too#2803
rubenvdlinde merged 2 commits into
developmentfrom
fix/pending-schema-ref-leaks-into-save

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

#2792 made the pending schema ref single-use and cleared it in find(). saveObject() and patchObject() do not go through find() — they call setContextFromParameters(), which was left as it was. So the leak is still live on the write path, which is where it is most visible.

Measured on development, after #2792

buildiq E2E, 2026-08-22 12:26 UTC (#2792 landed 11:09):

POST /index.php/apps/docudesk/api/templates

Failed to create template: Schema slug "application" is not carried by register
"docudesk" (id 19), which carries 9 schema(s). 1 schema(s) elsewhere on this
instance carry this slug; none of them is served here, because naming a register
makes it a boundary.

Docudesk asked for register 19 / schema 18 (template) — its seed writes that pair and verifies it on read-back immediately before. application is openbuild's schema, left pending by an unrelated earlier call on the same shared ObjectService instance. setRegister() consumed it and re-resolved it inside docudesk's register, which correctly refused a schema that register does not carry.

The refusal is right. The ref should never have been there.

The fix

When a caller supplies both register and schema it has named the pair outright, so any pending ref is stale by definition and is dropped before resolution — in the shared helper, rather than as a second copy of find()'s guard at the third call site.

  • Order-independence is untouched: setSchema($s)->setRegister($r) still re-resolves.
  • The boundary is exactly as strict: the schema is still resolved inside the register named here.

Verification

check result
new test, without the fix SchemaNotInRegisterException … "application" … register "docudesk" (id 19) — the production message verbatim
new test, with the fix OK (2 tests, 2 assertions)
tests/Unit/Service/ 8 errors with and without this change — pre-existing, measured both directions
phpstan / phpcs / phpmd [OK] No errors / 0 errors / clean

Fleet impact

Two failing E2E suites trace here: buildiq's template create (above) and integriq's flow halting immediately after its write step. Both are on development now.

Full diagnosis thread, including two wrong attributions of mine that this replaces, is on #2774. The server log is available on failing E2E runs as the nextcloud-log-e2e artifact (ConductionNL/.github#554) — that capture is what made this findable.

Conduction Release Bot added 2 commits August 22, 2026 17:05
#2792 made the pending ref single-use and cleared it in find(). saveObject() and
patchObject() do not go through find() — they call setContextFromParameters(),
which was left as it was. So the leak is still live on the write path, which is
where it is most visible.

MEASURED on buildiq's E2E, 2026-08-22 12:26 UTC (after #2792 landed at 11:09):

    POST /index.php/apps/docudesk/api/templates
    Failed to create template: Schema slug "application" is not carried by
    register "docudesk" (id 19), which carries 9 schema(s). 1 schema(s)
    elsewhere on this instance carry this slug; none of them is served here,
    because naming a register makes it a boundary.

Docudesk asked for register 19 / schema 18 (`template`) — the seed writes that
pair and verifies it on read-back immediately before. `application` is
openbuild's schema, left pending by an unrelated earlier call on the same
shared ObjectService instance. setRegister() then consumed it and re-resolved it
inside docudesk's register, which correctly refused a schema that register does
not carry. The refusal is right; the ref should never have been there.

find() already guards this, with the same reasoning in its own comment. The fix
belongs in the shared helper rather than a second copy at the third call site:
when a caller supplies BOTH register and schema, it has named the pair outright,
so any pending ref is stale by definition and is dropped before resolution.

Order-independence is untouched — a caller that genuinely does
setSchema($s)->setRegister($r) still re-resolves — and the boundary is exactly
as strict, because the schema is still resolved inside the register named here.

Two failing fleet E2E suites trace to this: buildiq's template create (above) and
integriq's flow halting immediately after its `write` step.
find() already has a regression test for this leak. saveObject() and
patchObject() do not go through find(), so nothing covered the path that was
still broken.

Drives setContextFromParameters() by reflection rather than saveObject(): the
leak lives entirely in context resolution, and the rest of a save needs a dozen
more collaborators that would obscure what is being asserted.

Paired control, against the parent commit's lib/:

  without the fix   SchemaNotInRegisterException: Schema slug "application" is
                    not carried by register "docudesk" (id 19) …
  with the fix      OK (2 tests, 2 assertions)

That exception is the production message verbatim, from buildiq's E2E on
2026-08-22 12:26 UTC.

The wider tests/Unit/Service/ suite reports 8 errors both WITH and WITHOUT this
change (LeafProvidersMetadataTest, DbalObjectSourceProviderTest,
TextExtractionServiceTest) — pre-existing, measured both directions rather than
assumed.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/openregister @ 3dad8ea

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-specs
test-l10n
format
composer ✅ 175/175
npm ✅ 528/528
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-22 15:55 UTC

Download the full PDF report from the workflow artifacts.

@rubenvdlinde
rubenvdlinde merged commit c7e7669 into development Aug 22, 2026
51 of 76 checks passed
@rubenvdlinde
rubenvdlinde deleted the fix/pending-schema-ref-leaks-into-save branch August 22, 2026 15:57
rubenvdlinde added a commit that referenced this pull request Aug 22, 2026
#2803 landed mid-flight — a FOURTH pass at this one mechanism, found from the
other end (a docudesk template create failing with openbuild's `application`
slug). It guards `setContextFromParameters()`, the path a caller naming BOTH
register and schema takes. This branch guards the seven public entry points.
The two are complementary, not competing: #2803 covers the "names both" case
wherever it is reached from, this covers every operation that supplies its own
scope at all.

Both test sets are kept. They are different regressions from different apps
against the same root cause, and either would have caught only its own.
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