Skip to content

finding(objectql): three more getAllObjects?.() ?? [] seams in plugin.ts invent an empty registry — one of them silently skips schema sync for every object at boot #9285

Description

@os-zhuang

Found while implementing #9154 (the roll-up summary index's registry read). Filed unassigned, not a claim. #9154's PR (#9284) deliberately does not touch these — its declared file surface was buildSummaryIndex / ensureSummaryIndexes in engine.ts, and a different file with a different verification surface is outside the in-place exemption.

Measured on origin/main @ 739fe5b79, located by symbol.

The seams

packages/objectql/src/plugin.ts, three sites, all the same shape #8895 / #9002 / #9154 ruled on — a read that could not run answered with an invented "the registry holds nothing":

  1. syncRegisteredSchemas() — the consequential one:

    constallObjects=this.ql.registry?.getAllObjects?.()??[];if(allObjects.length===0)return;

    An empty answer means the early return fires and no registered object's schema is synced to any driver — no table created, no column added — silently, at boot, with the plugin reporting a clean start. "The registry holds nothing" and "the registry could not be read" have opposite consequences here, and only the first one is a truthful reason to skip.

  2. reconcileFederatedBindings() — same spelling, then if (federated.length === 0) return;. This routine exists to NAME federated objects whose datasource is not reachable at boot; its own docblock says "a boot with nothing to report says nothing". An unreadable registry makes it report nothing, which is exactly the silence it was written to prevent.

  3. The governance inventory (runActionGovernanceInventory's objects argument) — (() => { try { return ql.registry?.getAllObjects?.() ?? []; } catch { return []; } })(). Both halves of the swallow, inline. The inventory audits action governance over the registered object set; an invented empty set audits nothing and says so nowhere.

Deliberately not included: the objectsRegistered: this.ql?.registry?.getAllObjects?.()?.length || 0 in the ObjectQL engine started log line. That one is judged benign rather than left unexamined — it is a count inside one advisory info log, and a wrong 0 there costs an advisory and no data.

Why this is filed as an observation, not a defect card

The same dormancy argument #9154 was graded on, re-derived on today's tree and still holding: SchemaRegistry.getAllObjects() is a walk over in-memory Maps calling resolveObject(), which returns undefined on every failure branch it models and never throws; the fold below it (foldExtendersOntoDefinition to mergeObjectDefinitions, scalarOverridesPackagedBase) is spreads and comparisons. No I/O, no driver, no throw on the measured path.

The caveat #9154 measured applies here too, and is not hypothetical: the optional call ?.() also absorbs a structural omission. Removing it from buildSummaryIndex turned 83 tests red across 9 suites whose vi.mock('./registry') doubles never modelled getAllObjects at all — an incomplete double had been indistinguishable from an empty registry for as long as those suites existed. Seams 1 and 2 above carry the same blind spot without needing a catch.

Suggested disposition (triage's call, not mine)

Seam 1 looks like the same answer #9154 got — propagate, because an unreadable registry is never truthfully "no objects to sync", and failing the boot is more truthful than starting with an unmigrated store. Seams 2 and 3 are diagnostics rather than guards, so the cheaper half may be enough there: whatever the verdict, the invented emptiness must not be silent.

Related: #9154 (the roll-up summary index, same file family, PR #9284), #9002 (the two delete-cascade seams), #8895 (the dependents-probe fix that ruled the family discriminate or propagate), #8845 (the gate blind spot that keeps this shape invisible to check:durability-log-level — re-measured on #9154's PR, census unchanged across the fix).


Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions