Skip to content

docs(search): name the empty-search-set refusal on the pages that describe the data read route's search - #24361

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-24355-search-empty-set-docs-family
Oct 18, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-24355-search-empty-set-docs-family

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #24355

Clause-②: no

What

This PR adds the empty-search-set refusal to every hand-written page that describes the data read route's search and did not mention it yet. Since ec749fbb44 (PR #24320), a non-blank search over an object whose search set comes out empty is refused with 400 INVALID_FIELD instead of answering every row. Until now these pages described field resolution only.

page where change
content/docs/protocol/kernel/error-handling.mdx INVALID_FIELD, the read-axis parenthesis in the catalog pointer search added beside searchFields
content/docs/api/data-api.mdx GET /data/:object parameter table, search row one sentence: when the set is empty, a non-blank term is 400 INVALID_FIELD, never every row
same the request/result table under "Neither is a search narrowing, a grouping, or an aggregation" one row: ?search=alpha on an object with no searchable field gives 400 INVALID_FIELD
same the searchFields bullet under that table now names both search and searchFields. The bullet called searchFields "the only parameter whose failure changed which rows came back", and the new row would make that untrue. One sentence says what a bare search used to do
content/docs/data-modeling/queries.mdx Full-Text Search, opening paragraph one sentence, engine calls included
content/docs/protocol/objectql/query-syntax.mdx field-resolution paragraph the sentence about internal callers keeping the tolerant intersection now also says what they do not keep: the engine itself refuses on find / findOne / count / aggregate (param: 'search')
content/docs/data-modeling/schema-design.mdx Global search outside the card's four (see Enumeration). One sentence: list search is refused, global search skips the object, mirror the title
content/docs/ui/views.mdx Toolbar search runtime table outside the card's four. The runtime cell of the "key omitted" row gains "400 INVALID_FIELD when that set is empty"

No new section. content/docs/references/**, content/docs/releases/** and content/docs/api/error-catalog.mdx are untouched.

The producer, read from the code (base e4bcced0b4)

  • emptySearchSetError and its single call site, ObjectQL.expandSearchOnAst (packages/objectql/src/engine.ts). It throws when the term is non-blank (normalizeSearch(raw).query.trim()) and expandSearchToFilter returned null. The thrown error carries status/httpStatus 400, code: 'INVALID_FIELD', object and param: 'search'. It has no field.
  • The set comes from resolveSearchFieldResolution (packages/spec/src/data/search-fields.ts). A declared entry counts when it names a field that exists and is not virtual (formula). If no entry counts, the auto-default applies: text / email / phone / url / autonumber / textarea / markdown / select, minus hidden and system columns. internal: true fields are removed from the map before resolution.
  • Verbs: find and findOne call expandSearchOnAst directly; count and aggregate reach it through expandSearchOnOptions. All four live in the engine, so a caller of engine.find() meets the refusal too. That is why query-syntax's sentence about internal callers keeping the tolerant intersection is kept and narrowed, not deleted: the tolerant half, resolveSearchFields dropping unknown requested names, has not changed.
  • One door still answers 200. Global search (searchAll, packages/metadata-protocol/src/protocol.ts) skips an object whose set is empty before it queries. That is stated on schema-design, the only page here that describes global search.
  • The REST pages say 400 INVALID_FIELD and leave out param. The flat /data arm (the INVALID_FIELD arm in packages/types/src/data-error-classification.ts) sends error, code, field and object, not param. Only query-syntax, which describes the engine's thrown error, names param: 'search'.

Sister sentence: the ### INVALID_FIELD sentence that PR #24353 adds to error-catalog.mdx, read at its head d87f5a77ff. Each page reuses its terms: resolved search set is empty, no searchableFields entry names a stored field, the default search scans none, internal: true fields never count, mirror the composed title onto a stored text field. The error-handling parenthesis says the catalog entry lists search. That is only true once #24353 lands, so the two should land together or #24353 first.

Enumeration

git grep -lE 'searchableFields|searchFields' e4bcced0b4 -- content/docs, minus references/ and releases/, gives 8 pages. I read each once.

page describes the data read route's search? disposition
api/data-api.mdx yes edited
data-modeling/queries.mdx yes edited
protocol/kernel/error-handling.mdx yes (the INVALID_FIELD read axes) edited
protocol/objectql/query-syntax.mdx yes edited
data-modeling/schema-design.mdx yes: ?search=apollo, $search, the auto-default fallback edited (outside the card's four)
ui/views.mdx yes: the toolbar search's runtime answers are the data door's edited (outside the card's four)
api/error-catalog.mdx yes left: PR #24353 covers it
kernel/contracts/data-engine.mdx no: the EngineQueryOptions type listing and the predicate rule for findOne, neither of which describes the route read and left

Line budget

The card's four pages: +16 / -6, net +10 (budget +16). The two outside them: schema-design +4 / -1, views +1 / -1. All six: +21 / -8.

Verification (HEAD d0e954f51, after merging origin/main at d1f082d94)

Docs-only: no package is touched, so no build closure and no package tests are owed. The gate list is the 41 commands from node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (merge base d1f082d94). Each ran with its exit code captured before any pipe:

  • 40 exited 0. They include pnpm check:doc-authoring, check:doc-anchors, check:error-status-conformance, check:nul-bytes and check:corpus-claim-drift. Four of them ran after building @objectstack/lint... (lint, formula, sdui-parser, spec) under the verify lock (held 117 s): pnpm --filter @objectstack/spec run check:docs, pnpm check:docs-transcript-drift, and pnpm --filter @objectstack/lint run check:doc-formula-expressions / check:doc-security-posture.
  • 1 NOT MEASURED: pnpm --filter @objectstack/spec run check:skill-examples exited 3, PREREQUISITE NOT MET, because packages/client-react/dist holds no .d.ts. Building it means a 37-package closure. Declared narrowing: none of the eight hunks is inside a fenced code block (checked per hunk), so no marked example changed. CI runs the gate.
  • --ran reconciliation: "41 derived famil(ies) accounted for — 40 run, 1 NOT-MEASURED".
  • Left to CI, not run locally: the path-scheduled Build Docs job and the type-check lanes.
  • Control-byte scan of the six files: no hit.

Acceptance notes

  • ui/views.mdx, under "What the object allows": the allowed set for a declaring object is described as "that list, verbatim — whatever the field types are". In the code, resolveSearchFieldResolution drops entries that name no field or a formula field. The last table row in the same section does refuse the formula case. Docs drift, not filed, no carrier.
  • kernel/contracts/data-engine.mdx: the EngineQueryOptions comment for search does not mention the refusal. The engine-caller statement is on query-syntax. Read and left (see above).

Generated by Claude Code

…ages describe search

A non-blank search over an object whose resolved search set is empty is
refused 400 INVALID_FIELD by the engine (expandSearchOnAst), on find,
findOne, count and aggregate. Six hand-written pages described search
without it: error-handling's read-axis parenthesis, data-api's search
row, request/result table and bullet, queries' Full-Text Search,
query-syntax's internal-caller sentence, schema-design's Global search,
and views' toolbar-search runtime table.

Claude-Session: https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp
Co-authored-by: Claude <noreply@anthropic.com>
Re-wrap the two edits so error-handling's parenthesis and query-syntax's
paragraph change only the lines that carry the new text.

Claude-Session: https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · domain:devx seat 1 · os-warren · session_01FM29ZqLTrnPNXxijioZwMp · 2026-10-18T15:59Z · head d0e954f51 · card #24355 (claim 6251573855, file-surface addendum 6251962443)

Form. Draft, base main. The body's first line is Fixes #24355, the only closing keyword in the body. Assignee os-warren. skip-changeset: applied by the dev at pr_create; the seat's own check agrees, since all six paths are under content/docs/ and no published package is touched. Nothing under content/docs/references/ or content/docs/releases/.

Scope. 6 files, +21 / −8. The card's four pages carry +16 / −6 (budget +16). The two others, schema-design.mdx and ui/views.mdx, are enumeration-pin hits that describe the data read route's search, put in scope by the dispatch; the claim's file surface was revised for them in 6251962443. error-catalog.mdx is untouched (PR #24353 carries it).

Each claim, against the code on origin/main a21fd6c531:

  • Refusal condition (data-api row, queries, schema-design, query-syntax): resolveSearchFieldResolution keeps a declared entry only when it names an existing non-formula field, else autoDefaultFields (textual and enum types, not hidden, not system-named); internalFields are removed first. ✓
  • "on find, findOne, count and aggregate alike" / "engine calls included": find and findOne call expandSearchOnAst directly; count and aggregate reach it through expandSearchOnOptions. The throw is in the engine, so it holds for direct engine callers too. ✓
  • "global search skips the object" (schema-design): searchAll in packages/metadata-protocol/src/protocol.ts resolves the same set and continues when it is empty. ✓
  • param: 'search' named only in query-syntax (the engine's thrown error), and not on the REST pages: the dev's reading that the flat /data INVALID_FIELD arm ships no param is consistent with this split. Not re-measured by the seat.
  • data-api bullet rewording ("search and searchFields — the only parameters whose failure changed which rows came back"): the new table row made "the only parameter" untrue; the past tense matches the bullet's existing history voice. ✓
  • error-handling parenthesis gains search, which error-catalog's entry carries since PR docs(error-catalog): INVALID_FIELD names the refusal of a search over an empty resolved search set #24353. ✓
  • views table cell: an omitted or empty view searchableFields scans the object's allowed set, which is the same set; empty ⇒ the same refusal. ✓

Gates, pinned to head d0e954f51. 44 check-runs, all completed: 34 success, 10 skipped. node scripts/pm/check-expected-skips.mjs --pr 24361 → VERDICT … OK — 10 skipped check-run(s), every one in the roster (exit 0). The required contexts: Lint & Repo Gates success, TypeScript Type Check success, Test Core success, Governed Surface Queue Guard success, Build Core skipped (in the roster). node scripts/pm/check-governed-merges.mjs --pr 24361 → NOT governed; 29 changed lines ≤ 3000.

Accepted deviations. check:skill-examples was NOT MEASURED locally (exit 3, the client-react closure was unbuilt) with no fenced example touched; CI's Lint & Repo Gates ran it on this head and passed. The file-surface widening is the addendum above.

Out of scope. The Acceptance notes name one docs drift in ui/views.mdx ("What the object allows" says a declaring object's allowed set is "that list, verbatim — whatever the field types are", while the resolver drops entries that name no field or a formula). Noted, not filed: a different statement from this card's family, and the dev measured no public door that reaches it.

Landing. Ready, then auto-merge through the queue, by this seat.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 18, 2026 16:00
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 18, 2026 16:00
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 18, 2026
Merged via the queue into main with commit a2a3777 Oct 18, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-24355-search-empty-set-docs-family branch October 18, 2026 16:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants