Uh oh!
There was an error while loading. Please reload this page.
fix(metadata-protocol): drop the invented-empty-registry swallow in global search - #11821
Conversation
…lobal search searchAll read the host registry as registry?.getAllObjects?.() ?? [], so a registry that cannot enumerate at all answered the caller with a successful empty sweep (zero hits, objectsScanned: 0). SchemaRegistry.getAllObjects() has no throwing path, so the guards only ever absorbed the structural omission — invisible by construction. Same disposition as the engine-side registry sweeps: drop both halves and let it propagate (ADR-0110 D3). Pins added beside the family's existing read-seam tests: the omission now rejects (TypeError, sweep never ran), a truthfully empty registry still resolves the empty response, and a blank query still short-circuits before the registry is consulted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VK8rFDtg8eREaxBGX99Csn
📓 Docs Drift CheckThis PR changes 1 package(s): ⛔ 1 release-owned page(s) name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 7 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 37f77283ae1eb675af5361efee9f9f552de83252 && git checkout 37f77283ae1eb675af5361efee9f9f552de83252
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e43b18fd9413b5774952eda5b852a54ef423ce2c 583b3ed268513771935440a62c4ae3421cbf4e43 && git checkout -B drift-repro e43b18fd9413b5774952eda5b852a54ef423ce2c && git merge --no-ff 583b3ed268513771935440a62c4ae3421cbf4e43
node scripts/docs-audit/affected-docs.mjs --json e43b18fd9413b5774952eda5b852a54ef423ce2c
|
Uh oh!
There was an error while loading. Please reload this page.
Closes#11754
What was measured
searchAllinpackages/metadata-protocol/src/protocol.tsread the host registry as(this.engine as any).registry?.getAllObjects?.() ?? []. An invented empty list made the whole sweep a silent no-op: zero hits,objectsScanned: 0, and a successful response.origin/mainat7e83932621before leaning on it:const objects: ServiceObject[] = this._registry.getAllObjects();atpackages/objectql/src/engine.ts7448 / 10562 / 10805, andgrep -c 'objects = []'over that file returns 0. The landed disposition is drop-both-halves-and-propagate.SchemaRegistry.getAllObjects()walks in-memoryMaps with no throwing path, so the?? []could never fire on an outage — what the guards absorbed is the structural omission (a host registry that does not implementgetAllObjectsat all), which never throws and is invisible by construction. This closes the last unswept member of the invented-empty-registry family (finding(objectql): the delete-cascade path's two registry-read swallows are the #8895 shape one layer up —catch → returndisables every referential guard at once, silently #9002, Measured set: five read seams answer a failed read from an empty accumulator with no log and no field saying the answer is incomplete #8896, finding(objectql): three moregetAllObjects?.() ?? []seams inplugin.tsinvent an empty registry — one of them silently skips schema sync for every object at boot #9285/PR fix(objectql): plugin registry reads discriminate unreadable from empty — schema sync no longer skips every object silently at boot (#9285) #9682), on the ruled discriminate or propagate answer (ObjectQL.cascadeDeleteRelations fails OPEN: a failed dependents probe skips therestrictguard entirely, so a delete that should be refused succeeds silently #8895): a registry that cannot enumerate its objects is never truthfully "no objects" (ADR-0110 D3). No reproduction and no user-visible defect are claimed.What changed
packages/metadata-protocol/src/protocol.ts— one hunk at the seam:const allObjects = this.engine.registry.getAllObjects();. Both?.and the?? []are gone; theas anycast went with them (registryis a declared member ofMetadataHostEngine). No new error code and no new response field: at the REST door the omission surfaces throughGET /search's existingcatch→mapDataErrorpath. This is an accept→reject flip on a public door for the broken-composition case only — a call that answered HTTP 200 with zero hits over an unenumerable registry now fails. The diff widens no public surface beyond that predicted flip.packages/metadata-protocol/src/protocol.read-seam-empty-accumulator.test.ts— four pins added beside the family's existing [Measured set: five read seams answer a failed read from an empty accumulator with no log and no field saying the answer is incomplete #8896] tests: the omission rejects (TypeError naminggetAllObjects, and the data read was never issued), a registry-less engine rejects, a registry that truthfully enumerates "no objects" still resolves the successful empty response, and a blank query still short-circuits before the registry is consulted..changeset/global-search-registry-propagate.md— patch,@objectstack/metadata-protocol.What was deliberately NOT changed
protocol.ts(~L75) — untouched by construction; the diff is one hunk at the seam, ~9,800 lines away from PR fix(spec): re-export the three types the root entry's own inferred types mention (#11350) #11716's emitted-specifier pin region. No import was added or reordered.packages/cli/src/utils/schema-migrate.ts:329— same spelling, but its docblock declares the degradation and its consumer is one advisory; examined and cleared by the filer (domain:cli).packages/objectql/src/plugin.ts:728— theobjectsRegisteredstart-log count, cleared explicitly by finding(objectql): three moregetAllObjects?.() ?? []seams inplugin.tsinvent an empty registry — one of them silently skips schema sync for every object at boot #9285. Neither of finding(objectql): three moregetAllObjects?.() ?? []seams inplugin.tsinvent an empty registry — one of them silently skips schema sync for every object at boot #9285 / fix(objectql): the roll-up summary index's registry read propagates, and a failed read is never cached as an empty index (#9154) #9284 / ObjectQL.cascadeDeleteRelations fails OPEN: a failed dependents probe skips therestrictguard entirely, so a delete that should be refused succeeds silently #8895 is addressed here; each remains as it was.Test-double fallout, with counts
Predicted in advance from #9284's nine suites; measured here: 0 suites went red. All nine candidate suites referencing
searchAllwere enumerated and either run or read: metadata-protocol (4 suites — doubles all implementgetAllObjects; the twoorderby-vocabularydoubles without it feed onlyauditMetaItem), objectql (4 suites — realObjectQLengines; the one minimalnoTxEnginedouble calls onlygetDiscovery), rest (3 suites — either mock the protocol object itself or use a fake engine that implementsgetAllObjects), runtime (1 suite — mocks the protocol). No double was fixed because none was broken; no suite was skipped, disabled, or quarantined.Verification (all at
583b3ed268, the branch head; exit codes captured before any pipe)pnpm --filter @objectstack/metadata-protocol exec vitest run --maxWorkers=2— 139 files / 1913 tests passed, 0 failed (2 files / 10 tests pre-existing skips).@objectstack/metadata-protocoldist and confirming the fix reached it:grep -c 'registry.getAllObjects()' dist/index.js= 1, swallow spelling = 0): objectql 4 suites 78 passed; rest 3 suites 64 passed; runtime 1 suite 23 passed.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, change set taken by the script from merge base7e8393262, derivation stamped at583b3ed268): all 16 path-matched + 5 convention-triggered gates green by their own exit codes, includingcheck:durability-log-level(its own verdict line: "66 read seam(s), none invents an unreported answer"),check:engine-double-contract,check:where-matcher,check:cross-package-test-inputs,check:nul-bytes("scanned 6590 text file(s) … no raw ASCII control bytes").pnpm check:type-check-debt --re-measureafterturbo run build --filter='./packages/*' --filter='./packages/*/*'(70/70 tasks): "32 ledger entr(ies) re-measured … none above its recorded number" — the new test code does not move metadata-protocol's frozen 63.pnpm lint(full repo eslint, not narrowed): exit 0.Ablation, with numbers
Fix committed first; mutation and restore both proven on disk by anchored
grep -cFcounts; restore ran undertrap … EXIT INT TERM.swallow-count=1,fixed-count=0on disk).vitest run src/protocol.read-seam-empty-accumulator.test.ts→ exit 1, 2 failed / 12 passed, both failures the exact pre-fix shape:expected a rejection, but the call resolved with {"query":"Acme","hits":[],"totalObjects":0,"totalHits":0,"truncated":false}.swallow-count=0,fixed-count=1,git status --porcelainempty../protocol.jsrelative (vitest resolves it tosrc/protocol.ts), and the package's full suite — new pins included — passed beforepackages/metadata-protocol/distexisted in this fresh worktree, which is only possible if the tests readsrc, notdist.Generated by Claude Code