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):
| package | single-arg call sites | test-layer tsc program? |
|---|
objectql | 55 | none — unmeasured |
rest | 14 | tsconfig.test.json — these are the ledgered TS2554 |
runtime | 9 | none — unmeasured |
plugins | 4 | none — 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
Filed unassigned and ungraded while paying down
packages/rest's test-typecheck ledger (part of #12573). ⛔ Not graded, not routed. Measured onorigin/main@527e0505d, workspace closure built.The fact
packages/objectql/src/registry.ts:1540requirespackageIdas its second positional parameter:Its sibling on the same class,
registry.ts:2828, declares the same thing optional — and both feed the identical downstream call:applyProtection(packages/spec/src/shared/protection.zod.ts:137) documents the package-less case as intended, not as an accident:So the no-
packageIdregistration 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):objectqlresttsconfig.test.json— these are the ledgered TS2554runtimeplugins⇒ the 14 in
packages/restare ledgered asTS2554: Expected 2-5 arguments, but got 1only 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
packageIdto satisfy the type changes what the fixture is:applyProtectionthen stamps_packageIdand_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 defaultspackageIdto'__runtime__'and mutates the schema by auto-assigningfield.namefrom 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, thez.inferOUTPUT type, instead ofServiceObjectInput) and was closed by PR #6786, which dropped the 135as anythat had been dodging it. That fix landed on the engine facade'sschemaparameter. The second parameter, on the raw registry method, was not part of it.as anyat the call site is how this seam gets dodged, and it hides genuinely misspelled keys too. Several of the 82 sites already carryas any/as neveron the schema argument.The reviewable decision
registerObjectis public API of@objectstack/objectql, so relaxingpackageIdtopackageId?: stringis 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:packageIdoptional onSchemaRegistry.registerObject, matchingregisterItemand the behaviourapplyProtectionalready documents. Widening a parameter breaks no existing caller; 82 call sites stop being latent errors._packageId/_provenancestamped), so it is a test-semantics change at 82 sites.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
Refs
packages/rest's new 37-error test-typecheck ledger — 13 of them are one class in one file, and 14 more look like one call-signature story across five #12573 /packages/resthas NO tsc program that compiles any test file — 149 test files unchecked, and AGENTS.md names this exact shape as how a@ts-expect-errorbecomes a phantom check #12542 — where thepackages/resttest-layer program and its ledger liveregisterObject的参数标成 schema 的 OUTPUT 类型(z.infer),合法的作者态字面量一律 TS2740 —— 这是 spec/client 两份测试层欠账的同一根因 #5543 / PR #6786 — the first parameter of this same method, fixed@ts-expect-error退役 pin 在packages/spec里是幽灵检查:tsconfig 把**/*.test.ts排除出唯一的tsc --noEmit#5286 / 幽灵@ts-expect-error不止 spec:@objectstack/client也有 1 处落在 tsconfig 排除区内(全仓横扫结果) #5449 — the sibling-program mechanism