Skip to content

finding(objectql): SchemaRegistry.registerObject requires packageId while its sibling registerItem declares it optional — 14 ledgered TS2554 in packages/rest and 68 identical call sites in packages no tsc program reads #12623

Description

@os-litant

Filed unassigned and ungraded while paying down packages/rest's test-typecheck ledger (part of #12573). ⛔ Not graded, not routed. Measured on origin/main @ 527e0505d, workspace closure built.

The fact

packages/objectql/src/registry.ts:1540 requires packageId as its second positional parameter:

registerObject(schema: ServiceObject,packageId: string,// ← required, no defaultnamespace?: string,ownership: ObjectOwnership='own',priority: number=/* … */): string

Its sibling on the same class, registry.ts:2828, declares the same thing optional — and both feed the identical downstream call:

registerItem<T>(type: string,item: T,keyField: keyofT='name',packageId?: string){// …applyProtection(itemasany,{ packageId });// registry.ts:2839}// registerObject does the same at registry.ts:1691:applyProtection(schemaasany,{ packageId });

applyProtection (packages/spec/src/shared/protection.zod.ts:137) documents the package-less case as intended, not as an accident:

"Only stamp provenance when we actually have package coords or an author-facing protection block; otherwise leave the item alone so that DB-only / test fixtures don't acquire an unexpected _provenance field. The loader passes packageId for genuine package items; bare registerItem(type, item) calls without a package context still produce a clean item."

So the no-packageId registration is a supported shape that one of the two entry points cannot express.

Blast radius, measured

82 single-argument registry.registerObject(x) call sites, repo-wide (instrument: grep -rnE "registry\.registerObject\([^,()]*(\([^()]*\))?[^,()]*\);" --include=*.ts packages/; control: 254 multi-argument one-line calls on the same instrument, so the count is a reading and not a broken pattern):

packagesingle-arg call sitestest-layer tsc program?
objectql55none — unmeasured
rest14tsconfig.test.json — these are the ledgered TS2554
runtime9none — unmeasured
plugins4none — unmeasured

⇒ the 14 in packages/rest are ledgered as TS2554: Expected 2-5 arguments, but got 1 only because that package is one of five with a test-layer program. The other 68 are the same latent error at the same seam, sitting where no gate reads them.

Why this is not a test-side repair

Supplying a packageId to satisfy the type changes what the fixture is: applyProtection then stamps _packageId and _provenance: 'package' onto the registered object, which is exactly the state the helper's comment says a package-less registration deliberately avoids. Routing through the engine facade instead (engine.registerObject(x), engine.ts:12350) is not neutral either — it defaults packageId to '__runtime__'and mutates the schema by auto-assigning field.name from each key.

Either way the repair would be to a fixture's data, not its annotation, which is why #12573's PR leaves all 14 unrepaired and reports them instead.

Prior art on this exact method

#5543 found the first parameter of this same method mis-annotated (ServiceObject, the z.infer OUTPUT type, instead of ServiceObjectInput) and was closed by PR #6786, which dropped the 135 as any that had been dodging it. That fix landed on the engine facade's schema parameter. The second parameter, on the raw registry method, was not part of it.

⚠️ Note the shape #5543 called out: as any at the call site is how this seam gets dodged, and it hides genuinely misspelled keys too. Several of the 82 sites already carry as any / as never on the schema argument.

The reviewable decision

registerObject is public API of @objectstack/objectql, so relaxing packageId to packageId?: string is a contract-surface change and belongs to a maintainer, not to a test-layer paydown — the same reasoning #5543 gave for not fixing it inline. The options:

  • A. Make packageId optional on SchemaRegistry.registerObject, matching registerItem and the behaviour applyProtection already documents. Widening a parameter breaks no existing caller; 82 call sites stop being latent errors.
  • B. Keep it required and change the 82 call sites to pass a package id — this alters what every one of those fixtures registers (_packageId / _provenance stamped), so it is a test-semantics change at 82 sites.
  • C. Keep it required and leave the 68 unmeasured sites as they are; packages/rest's 14 stay ledgered.

No urgency claimed; nothing here is newly broken — the errors are latent everywhere except the one package that put a tsc program in front of its tests.

Re-check

grep -rnE "registry\.registerObject\([^,()]*(\([^()]*\))?[^,()]*\);" --include=*.ts packages/ | wc -l
sed -n '1540,1550p' packages/objectql/src/registry.ts
sed -n '2828,2840p' packages/objectql/src/registry.ts

Refs

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions