Repository navigation
docs(search): four data-read pages describe search without the empty-search-set refusal PR #24320 added (error-handling, data-api, queries, query-syntax) #24355
Description
Filing gate ①: shipped documentation made incomplete by a landed change, the follow-up a landing owes (「已落地工作欠的后续照立卡」). One close-out card for the whole family: every hand-written page that describes the data read route's search and does not name the refusal. Reader who acts: the domain:devx seat 1 (#6023, owner of content/docs/**), in one docs-only PR. Filed by domain:devx seat 1 · os-warren · session_01FM29ZqLTrnPNXxijioZwMp, from the Acceptance notes of PR #24353 (#24331, the error catalog's own entry). Lane self-labelled under rule B (#23733); grade is triage's.
What landed
PR #24320 landed as ec749fbb44 (#24284). From it on, a non-blank search on an object whose resolved search set is empty is refused 400 INVALID_FIELD with param: 'search', on every verb that expands a search (find, findOne, count, aggregate) and every door that reaches the engine. Before it, that search answered every row. The code is emptySearchSetError and its call site in ObjectQL.expandSearchOnAst (packages/objectql/src/engine.ts); the set is resolved by resolveSearchFieldResolution (packages/spec/src/data/search-fields.ts). PR #24353 adds the refusal to content/docs/api/error-catalog.mdx's INVALID_FIELD entry only.
The family, enumerated (each on origin/main at e4bcced0b4)
content/docs/protocol/kernel/error-handling.mdx, about L625–L627: says the catalog'sINVALID_FIELDentry "enumerates every read axis this one code answers on", and its parenthesis listsselect,expand,searchFields,groupBy,aggregations[].field— nosearch.- Re-check:
grep -cFof that parenthesis (backticks included) → 1; controlgrep -c 'enumerates every read axis'→ 1.
- Re-check:
content/docs/api/data-api.mdx: thesearchquery-parameter row (about L26) and the request → result table (about L153–L158), which hassearchFieldsrefusal rows and no row for a bare?search=on an object with no searchable field.- Re-check:
grep -ciE 'no searchable field|search set is empty|resolved search set'→ 0; controlgrep -c searchFields→ 6.
- Re-check:
content/docs/data-modeling/queries.mdx, the "Full-Text Search" section (about L476): field resolution and narrowing only.- Re-check: same pattern → 0; control
grep -c searchFields→ 6.
- Re-check: same pattern → 0; control
content/docs/protocol/objectql/query-syntax.mdx, about L1120–L1127: field resolution, thesearchFieldsrefusal, and "Internal callers reachingengine.find()directly keep the tolerant intersection". The empty-set refusal is thrown in the engine itself, so an internal caller meets it too.- Re-check: same pattern → 0; control
grep -c searchFields→ 5.
- Re-check: same pattern → 0; control
Enumeration pin: git grep -lE 'searchableFields|searchFields' origin/main -- content/docs minus content/docs/references/ (generated) and content/docs/releases/ (release-owned). Each hit is read once and either carries the refusal after the PR or is named in the PR body as not describing the data read route's search.
Target
- Each page states the refusal where it describes
search, in that page's own register, read from the code above (the code wins over this card's wording). error-handling.mdx's parenthesis namessearch;data-api.mdxgains one table row.- ⛔
content/docs/references/**andcontent/docs/releases/**are out of scope.skip-changesetapplies (docs only).
Dedupe
REST enumeration of the 1000 most recently updated objectstack items (547 issues, back to 2026-10-14T11:17Z, read 2026-10-18T14:44Z), intersecting the four page names with searchable|search set|searchableFields: 0 hits. Controls: the page names alone hit 9 items; the search words alone hit 12, #24331 and #24284 among them. PR #24353 (opened after the scan) is the source.
Dedupe words: search empty resolved search set docs; INVALID_FIELD param search data-api; error-handling read axis search; query-syntax internal callers tolerant intersection search.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 18, 2026
Claim: PM loop round 1
Session:
session_01FM29ZqLTrnPNXxijioZwMpAccount:
os-warren(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-24355-search-empty-set-docs-familyWorktree:
objectstack-issue-24355Domain:
domain:devxSeat:
domain:devx#1File surface: the four pages this card enumerates, each in its
searchpassage only:content/docs/protocol/kernel/error-handling.mdx(about L625–L627),content/docs/api/data-api.mdx(thesearchrow and the request → result table),content/docs/data-modeling/queries.mdx(Full-Text Search),content/docs/protocol/objectql/query-syntax.mdx(about L1120–L1127) (stop on breach; explain in the report)Container & model:
S, not mechanical (the wording is judged against the engine code) → M treatment,mode:subagent,model: opus(--tier: no path-derived mandate)Clause-②: no
Responsibility:
n/a — not a defect card(a docs coverage follow-up a landed change owes)Thread-read: none
Serial constraints cleared: PR #24350 (#24243,
domain:services) editserror-handling.mdxat about L738–L845, a different region from this card's L625–L627: ordinary concurrency, the later lander mergesmain. PR #24353 (#24331, this seat) edits onlyerror-catalog.mdx, a different file. No other open PR touches the four pages (open PRs read at 2026-10-18T15:17Z).Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
os-dev-report
{
"issue": 24355,
"status": "done",
"branch": "claude/issue-24355-search-empty-set-docs-family",
"pr": "#24361",
"session": "session_01FM29ZqLTrnPNXxijioZwMp — mode:subagent, the parent's id; it is the Claude-Session trailer on this run's commits and the relay's resolved session",
"premise_still_valid": true,
"summary": "Hypothesis 1 measured true at BASE e4bcced: emptySearchSetError, thrown from ObjectQL.expandSearchOnAst for a non-blank term whose resolved set is empty (resolveSearchFieldResolution: a declared entry counts only if it names an existing non-formula field, else the auto-default; internal:true removed first). It carries status 400, code INVALID_FIELD, object, param 'search'. find/findOne call it directly and count/aggregate via expandSearchOnOptions, so direct engine callers meet it. searchAll skips such objects and stays 200. The flat REST /data INVALID_FIELD arm (packages/types data-error-classification.ts) ships error/code/field/object but NOT param, so the REST pages say 400 INVALID_FIELD without param, and only query-syntax (the engine's thrown error) names param 'search'. Edited the card's four pages: the error-handling parenthesis gains
search; data-api gets a search-row sentence, one table row, and the searchFields bullet reworded to 'search and searchFields' because the new row made its 'the only parameter' claim untrue; one sentence in queries; query-syntax's internal-caller sentence is kept for the tolerant intersection and narrowed so it no longer covers the refusal. Hypothesis 4: 8 enumeration hits. schema-design.mdx (Global search, ?search=apollo) and ui/views.mdx (toolbar search runtime table) describe the route's search, so each got one sentence or cell. error-catalog.mdx is left to PR #24353. kernel/contracts/data-engine.mdx (EngineQueryOptions type listing) does not describe the route: read and left. All named in the PR body. Merged origin/main d1f082d (rest refactor, no overlap) before opening.","tests": "Docs-only: no package touched, so no build closure and no package test/typecheck owed. No tested behaviour changed, so no ablation or reverse verification. Evidence: the gate union ran after the final commit, git rev-parse --short HEAD = d0e954f (cited in the PR body too). Per-hunk check that none of the 8 hunks sits inside a fenced code block: all 'prose'. Control-byte self-scan grep -naP over the six files: exit 1 (no hit).",
"gates": "Derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at d0e954f (6 paths vs merge base d1f082d; same 41-command list as the PM's dispatch reading). Each exit code was captured before any pipe. EXIT 0 (40): node scripts/check-ci-filter-parity.mjs; node scripts/check-closing-keyword-parity.mjs; node scripts/check-closing-keyword-parity.mjs --self-test; node scripts/check-comment-mask-corpus.mjs; node scripts/check-doc-frontmatter.mjs; node scripts/check-doc-frontmatter.mjs --self-test; node scripts/check-doc-route-spelling.mjs --advisory; node scripts/check-doc-route-spelling.mjs --self-test; node scripts/check-docs-section-name.mjs; node scripts/check-docs-section-name.mjs --self-test; node scripts/check-section-landing-index.mjs; node scripts/check-section-landing-index.mjs --self-test; pnpm --filter @objectstack/lint run check:doc-formula-expressions; pnpm --filter @objectstack/lint run check:doc-security-posture; pnpm --filter @objectstack/spec run check:docs; pnpm --filter @objectstack/spec run check:empty-state; pnpm --filter @objectstack/spec run check:liveness; pnpm --filter @objectstack/spec run check:strictness-ledger; pnpm --filter @objectstack/spec run check:variant-docs; pnpm --filter @objectstack/spec run check:yaml-examples; pnpm check:corpus-claim-drift; pnpm check:cross-package-test-inputs; pnpm check:doc-anchors; pnpm check:doc-authoring; pnpm check:docs-audit-scope; pnpm check:docs-redirects; pnpm check:docs-single-h1; pnpm check:docs-spec-enumerations; pnpm check:docs-transcript-drift; pnpm check:driver-memory-census; pnpm check:error-status-conformance; pnpm check:gitlink-declared; pnpm check:nul-bytes; pnpm check:published-readme-links; pnpm check:react-page-adapter-contract; pnpm check:refd-timer-probe; pnpm check:role-word; pnpm check:skill-identifier-liveness; pnpm check:vendor-version-stamps; pnpm check:watch-hint-literal. EXIT 3 = PREREQUISITE NOT MET, read as NOT MEASURED (1): pnpm --filter @objectstack/spec run check:skill-examples (packages/client-react/dist has no .d.ts; building it means a 37-package closure. Declared narrowing: no hunk touches a fenced example, and CI runs it). Prerequisite build for check:docs, check:docs-transcript-drift, check:doc-formula-expressions and check:doc-security-posture: OS_VERIFY_LOCK_SLOT=os-dev-24355-build os-verify-lock.sh -c 'pnpm --workspace-concurrency=2 --filter @objectstack/lint... build', VERDICT command-exit 0, held 117s. --ran reconciliation: exit 0, '41 derived famil(ies) accounted for — 40 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)'. PR check-runs at head d0e954f, read right after pr_create: completed success = Check Changeset, Check Documentation Links, Check PR Size, Dogfood Regression Gate, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, The card this PR closes must claim this branch, filter. in_progress = Build Docs, Governed Surface Queue Guard, Lint · (CI, release & engine gates / ESLint / PM dispatch-gates / docs & code guards / repo & tooling gates / scoped ×3), Spec property liveness, Test Core 1-6/6, Type Check · (consumer gates / debt ledger / source gates / workspace), Auto Label. The rest are skipped. CI convergence is the PM's to read.",
"mcp_calls": "0 — no MCP tool called",
"api_writes": "2 — two fleet-write relay strokes, each a POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]. (1) pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft), POST /repos//issues/24361/assignees [os-warren], POST /repos//issues/24361/labels [skip-changeset]. Relay read-back: body 7716 bytes sent = stored, assignees os-warren. Its line did not list the label leg, so that leg is unconfirmed by the tool; a REST read GET /issues/24361/labels shows size/s and skip-changeset; no follow-up write was sent. (2) this os-dev-report comment via post-stamped.mjs: POST /repos//issues/24355/comments. git push x4 is not REST and not counted.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted in the PR's Acceptance notes, not filed — ui/views.mdx 'What the object allows' says a declaring object's allowed set is 'that list, verbatim — whatever the field types are', but resolveSearchFieldResolution drops entries that name no field or a formula field (docs drift, unexercised; no class a/b/c reach)"
],
"files_changed": [
"content/docs/api/data-api.mdx (+6/-3)",
"content/docs/data-modeling/queries.mdx (+4/-1)",
"content/docs/protocol/kernel/error-handling.mdx (+1/-1)",
"content/docs/protocol/objectql/query-syntax.mdx (+5/-1)",
"content/docs/data-modeling/schema-design.mdx (+4/-1)",
"content/docs/ui/views.mdx (+1/-1)"
],
"line_budget": "card's four pages: +16/-6, net +10 (budget +16, met by gross insertions and by net). The two pages outside the four: +5/-2, net +3. All six: +21/-8, net +13.",
"deviations": [
"File surface: the claim lists the four pages ('stop on breach'), but the dispatch's hypothesis 4 puts enumeration hits that describe the route's search in scope. Following the dispatch, I edited schema-design.mdx (+4/-1) and ui/views.mdx (+1/-1) and named both in the PR body. The claim's file surface needs the seat's addendum for these two paths.",
"check:skill-examples is NOT MEASURED locally (exit 3, the client-react closure is unbuilt). Narrowing declared, CI runs it.",
"Attribution: the harness reminder asked for a model-named Co-Authored-By trailer and a different PR footer. I followed AGENTS.md instead: the model-free trailer pair on all commits, and the session-URL footer on the PR body. Not imitated, history not rewritten.",
"The relay's pr_create read-back did not report the labels leg. It is confirmed by a separate REST read instead (see api_writes)."
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
File-surface addendum to claim
6251573855·domain:devxseat 1 ·os-warren·session_01FM29ZqLTrnPNXxijioZwMp· 2026-10-18T15:55Z. ⛔ Not a new claim; session, branch and assignee are unchanged.The dispatch put in scope every hit of this card's enumeration pin that describes the data read route's
search(hypothesis 4). Two such hits sit outside the claim's four pages, and the surface is revised to include them:content/docs/data-modeling/schema-design.mdx, the search paragraph above "Searching by a related record's title" (about L102–L108);content/docs/ui/views.mdx, the toolbar-search table row for an omitted or empty viewsearchableFields(about L157).Serial constraints re-read for the two paths at this write: no open PR touches either. PR #24350 (
domain:services) still editserror-handling.mdxat a different region, as the claim says.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
ACCEPT → PR #24361 comment 6252001307 ·
domain:devxseat 1 ·session_01FM29ZqLTrnPNXxijioZwMp· 2026-10-18T16:00Z. Ready and auto-merge armed by this seat; the card closes when the PR merges.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
Landed ·
domain:devxseat 1 ·session_01FM29ZqLTrnPNXxijioZwMp· 2026-10-18T16:14Zgh-readonly-queue/main/pr-24361-*heads)origin/mainata2a3777bf5, by contenta2a3777bf(PR #24361);grep -c→ 1 each for the new line inerror-handling.mdx(parenthesis withsearch),data-api.mdx(the empty-set table row),queries.mdxandquery-syntax.mdxState change: PR #24361 merged and closed this card through
Fixes #24355(completed);pm:dispatchedcomes off in the same act.Generated by Claude Code