Repository navigation
fix(objectql,metadata-protocol)!: an in-process search-field override that names no searchable field is refused, never widened to a default set (#28120) - #28175
Conversation
… that names no searchable field is refused, never widened to a default set The stored-metadata reader seam (`narrowStoredMetadataSearch`) read an explicit search-field list naming no field as "names none" and wrote the narrowed default set over it, so `searchFields: []` from a host handler was served where the door and the engine refuse it. It now passes such a list to the engine, whose own refusal answers (`400 VALIDATION_ERROR`). The engine refuses a `searchFields` / `search.fields` override that names none of the fields its search scans with `400 INVALID_FIELD`, the door's code, before any driver read, where `resolveSearchFields` dropped the names and fell back to the whole default set. An override naming some searchable field keeps its other names intersected away, and an object with no searchable field keeps its own refusal. Fixes #28120 Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cyi29iG8J3UUXr68AZ
…-unknown search-override fallback `engine-search-empty-set` and `internal-field-positions` asserted that an override naming only fields outside the allowed set fell back to that set. They now assert the refusal, and the second keeps a partly-known override's intersection as its control. The query-syntax page says what the engine now does with such an override. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cyi29iG8J3UUXr68AZ
|
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia Contract-review-claim: The lane seat runs the |
📓 Docs Drift CheckThis PR changes 3 package(s): 18 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 6 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 171 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 b3095eb9f9ca96f9c4b48bcd5946cd562bf79c6f && git checkout b3095eb9f9ca96f9c4b48bcd5946cd562bf79c6f
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin a3d24c991f2d793a1600e7f25fb0d5f577ed2fff 872e1533357df9da75636f82d0487376ea537a0b && git checkout -B drift-repro a3d24c991f2d793a1600e7f25fb0d5f577ed2fff && git merge --no-ff 872e1533357df9da75636f82d0487376ea537a0b
node scripts/docs-audit/affected-docs.mjs --json a3d24c991f2d793a1600e7f25fb0d5f577ed2fff
|
…naming no searchable field The full-text search callout said direct `engine.find()` callers were unaffected by the override refusals; they now get both. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cyi29iG8J3UUXr68AZ
|
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia Contract-review-claim: The lane seat runs the |
…o-search case it leaves unjudged Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cyi29iG8J3UUXr68AZ
|
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia Contract-review-claim: The lane seat runs the |
|
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia Contract reviewServed-tier: Read on this head: card #28120 (body, triage 6524748197), card #28121 (closed as folded), PR #28108 (body) and its record 6523659371 (S1, S3), PR #28175 (body, 10-file list, net diff Check-runs at my reading of ① Derived judgmentsAccept-set changes the diff implies, each read in the code:
② Semver level
③ Boundary flagsPR author
Rule-level check of the PR body, the changeset and the test names: each states only what is now refused and what is unchanged; none gives a probe or a reach path. The earlier should-fix S1 on this record (the changeset's Before bullet) is resolved at ④ Design judgmentThe narrowing, read against the published spec on Tried to break it: both slots with conflicting values ( Findings: Must-fix: none. Should-fix: none outstanding. S1 (changeset Before bullet at rule level) and S2 (the no- Implemented-by: VERDICT: PASS |
|
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia Seat ACCEPT · PR #28175 · head
|
Requested by Elon · project thread
domain:server#3 · os-elon-musk · session_016WV4cyi29iG8J3UUXr68AZ
Fixes #28120 (family card; folds #28121, closed as folded into it)
Clause-②: no (narrowing) — two in-process tolerances the Data API doors already refuse are closed: the stored-metadata reader seam stops narrowing a search-field override that names no field and hands it to the engine's refusal, and the engine refuses an override naming none of the fields its search scans. No export, answer member, status or error code is added; the refusals are the doors'
VALIDATION_ERROR/ 400 andINVALID_FIELD/ 400.Metadata-protocol: none
维护者速读
searchFields覆盖,凡是 REST 门会拒的两种值,进程内现在也拒:(a) 经存储元数据读缝的空列表,不再被改写成默认集合,交给引擎按 400VALIDATION_ERROR拒;(b) 全部字段名都不可搜(拼错、改名、内部字段)的覆盖,引擎按 400INVALID_FIELD拒,不再退回整个默认集合去搜。packages/objectql/src/engine.ts的refuseSearchOverrideNamingNoSearchableField,packages/metadata-protocol/src/protocol.ts的narrowStoredMetadataSearch。pnpm --filter @objectstack/objectql exec vitest run src/engine-empty-search-fields-refusal.test.ts,预期 47 passed。What changes
(a) The stored-metadata reader seam (
@objectstack/metadata-protocol,narrowStoredMetadataSearch). An explicit search-field list that names no field (an empty array, a blank or comma-only string, an array with no string entry) is now passed through untouched, so the engine's own refusal answers it. Before, it was read as "names none", narrowed, and the narrowed default set was written over the caller's list, sosearchFields: []from a host handler onsys_metadata/sys_metadata_historywas served. The door never reaches this with such a list: its ingress gate (assertSearchFieldsAreSearchable) refuses it first, unchanged. The docblock now says what is true: an empty override is refused by the engine, not read as absent.refuseOrNarrowStoredMetadataEvaluateinpackages/runtimecalls this function and needed no change. A non-string, non-array value is still narrowed, as before.(b) The engine (
@objectstack/objectql,expandSearchOnAst). AsearchFieldsor object-formsearch.fieldsoverride that names none of the fields the expansion scans is refused400 INVALID_FIELD(param,field/fieldsnaming what the caller wrote,object) before any driver read, onfind,findOne,countandaggregate. "Scans" is judged on the set the expansion resolves over (resolveSearchFieldResolutionover the fields left after theinternal: trueand credential withholds), so this refuses exactly the overridesresolveSearchFieldsused to drop and widen back to the whole set, including an override naming only unknown fields on the stored-metadata family, which is now refused rather than resolved to the default set. Kept as it was:searchterm on an object with no searchable field is dropped silently, so the read answers every row (an empty auto-default search set is not refused) #24284 refusal still answers, naming that cause;nulloverride, and a non-array, non-string value.@objectstack/spec,resolveSearchFields. Docblock only: it records that the engine now refuses the fallback's input before this runs. The function itself is unchanged (it is spec's public helper and stays non-throwing).Docs.
content/docs/protocol/objectql/query-syntax.mdx: the sentence saying direct engine callers keep the tolerant intersection now says one naming no searchable field is refused.Why
Both halves come from the contract review of PR #28108 (record 6523659371, S1 and S3) and share one defect: an in-process override that the doors refuse was widened to a default set instead of refused. Triage 6524748197 graded it one family card,
Clause-②: no (narrowing).Verification
Premises measured first on
main277275aa(fix files reset to main and rebuilt), in-process, through the seam over a realObjectQLwith the realSysMetadataObjectand a driver double recording each read: (a)searchFields: [],' , 'andsearch.fields: []through the seam were served (narrowed set handed to the driver); (b) an all-unknown override widened to the default set on an ordinary object and on the family. REST callers: unchanged, refused byassertSearchFieldsAreSearchable(the door-parity cases below).packages/runtime/src/stored-metadata-reader-seam-search-override.test.ts(new, 15, seam over a real engine,find/findOne/count): the empty array, comma-only string and empty object-form list are refused400 VALIDATION_ERRORbefore any driver read; an all-unknown override is refused400 INVALID_FIELDbefore any driver read; controls: a named override scans only what it names, an absent override is still narrowed off the body and hash columns.packages/objectql/src/engine-empty-search-fields-refusal.test.ts(+32, all four verbs): all-unknown array, comma string, object form and a list with no string entry are refused400 INVALID_FIELDbefore any driver read; the refusal names the field; the REST door answers the same status and code; controls: partly-known override narrows to its known names, valid and absent overrides answer as before, an object with no searchable field keeps the metadata-protocol / objectql: asearchterm on an object with no searchable field is dropped silently, so the read answers every row (an empty auto-default search set is not refused) #24284 refusal.engine-search-empty-set.test.ts(an override intersected to nothing) andinternal-field-positions.test.ts(an override naming only aninternal: truecolumn; a partly-known one is still intersected away, never scanning it).mainand rebuilt): seam fix out, engine fix in ⇒ 6 seam cases red (the empty-list cases), engine pins green; engine fix out, seam fix in ⇒ 14 engine cases red and 3 seam cases red (the all-unknown cases); both in ⇒ all green. Each half's pin bites on its own.pnpm --filter <pkg> typecheckgreen for objectql, metadata-protocol, runtime, spec. Dogfood battery, everydispatch-gates --commandsfamily andpnpm lint: results posted on the card with the os-dev report.Skills-docs:
git grep -n -i searchFields -- skills content/docsread for wording on the engine's tolerance:content/docs/protocol/objectql/query-syntax.mdxsaid direct engine callers keep the tolerant intersection, updated here.skills/objectstack-query/SKILL.mddescribes the REST refusal only, still true;content/docs/api/data-api.mdxdescribes the door's history, still true. Generatedcontent/docs/references/**untouched.Out of the order's file surface, left as is: the
assertSearchFieldsAreSearchabledocblock inprotocol.ts(~L12546) explains the door's gate by the engine's old fallback; it stays true ofresolveSearchFieldsitself and is outsidenarrowStoredMetadataSearch.Human Minimum
packages/specholds no runtime logic.pnpm lintpasses. (No formatter ran; the lint reading is posted on the card with the gate battery.)main, one pull request.packages/spectouched: its generated artifacts are regenerated and committed. (Docblock only; the spec artifact gates' reading is posted on the card with the gate battery.)content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.🤖 Generated with Claude Code
https://claude.ai/code/session_016WV4cyi29iG8J3UUXr68AZ