docs(ui): the searchableFields boundary is allowed-set membership, not field type (#6897) - #6922
Merged
os-project-manager merged 1 commit intoAug 9, 2026
Conversation
…t field type (#6897) views.mdx:106 said a lookup in a view's `searchableFields` is always refused. Measured at both layers, that is false: the boundary is membership in the object's server-resolved allowed set, and field TYPE is consulted only on the auto-default branch (the object declares nothing). On an object declaring `searchableFields: ['subject', 'account_id']`, a view narrowing to the lookup `account_id` is ACCEPTED and scanned, while a `text` column the object left out is REFUSED — the exact inverse of a type-based reading. An author following the old row would delete a narrowing that works. The row now states the set-membership rule and links to a new `### Toolbar search (searchableFields)` section that mirrors the terminology landed in skills/objectstack-ui/SKILL.md by PR #6898, so the two corpora agree. The dotted-path half of the old row was correct and is kept. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F8q5J1MQyocgtNspb15fSn
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
os-project-manager
marked this pull request as ready for review
August 9, 2026 04:36
os-project-manager
deleted the
claude/issue-6897-views-searchable-set-membership
branch
August 9, 2026 04:52
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #6897
Premise: valid, and re-measured rather than trusted
The card was filed ~1h before dispatch, so I re-measured both directions from scratch at
both layers before touching prose — the dispatch order and the card body were treated
as leads, not as the record.
content/docs/ui/views.mdx:106(landed by PR #6670) said:The dotted-path half is correct. The lookup half is false, and it is false in a way that
is self-contradictory in its own terms: a lookup is one of the object's own columns.
The real boundary is membership in the object's server-resolved allowed set
(
resolveSearchFieldResolution,packages/spec/src/data/search-fields.ts:119), whosedeclared branch is
searchableFields?.filter((f) => all[f])— filtered by existence,never by type. The type lists (
SEARCHABLE_TEXTUAL_TYPES/SEARCHABLE_ENUM_TYPES) arereached only from
autoDefaultFields, i.e. the no-declaration branch.Why it matters, concretely: an author following the old row would delete a narrowing
that works, and an AI author would refuse to emit one.
Measured — the REAL ingress gate, four directions
Object
support_casedeclaringsearchableFields: ['subject', 'account_id']whereaccount_idis{ type: 'lookup', reference: 'crm_account' }, plus a twinopen_casewith the identical field map and no declaration. Driven through a real
ObjectQLengine and
ObjectStackProtocolImplementation.findData(soassertSearchFieldsAreSearchablereally runs), with two rows planted so the term lives inexactly one column each —
c1carries it only in the lookup,c2only in the text columnthe object left out. That planting is what makes "accepted" mean scanned rather than
merely not thrown.
searchFields: ['account_id']onsupport_case['c1']searchFields: ['account_name']onsupport_casestatus: 400,code: 'INVALID_FIELD',field: 'account_name'open_case['c2']), lookup refused by TYPEsearchFields: ['account_id.name']A and B are the pair the card names: a
textcolumn refused and alookupaccepted onthe same object, which no type-based reading can produce. C is what makes A and B mean
something rather than read as a coincidence — it shows the type list is real, just
reachable only from the other branch.
The two verbatim runtime messages, which are themselves the boundary stated twice:
Lint layer, re-run rather than trusted — the "SKILL.md parity (#6675)" block landed by the
merged PR #6898, including the two directions above:
The measurement harness was a scratch file, run in the foreground and deleted before the
commit;
git statusis clean apart from the one edited page.What changed — one file,
content/docs/ui/views.mdxsearchableFieldsrow now states the rule as set membership and stopsprescribing against a supported configuration. It also gets shorter: the cell was
already the longest in the table, and the correct rule needs a two-row table to state
honestly, so the cell links down instead of trying to carry it.
### Toolbar search (searchableFields)section, immediately after theproperties table. The dispatch asked whether the surrounding prose repeats the same
type-first error — on this page it does not; line 106 was the only occurrence. But a
corrected one-line cell would still have left the page teaching nothing about which
set is meant, which is the mental model the card says is the real damage. The section
carries: the allowed-set table (declared vs auto-default), the both-directions sentence
(declared lookup accepted / outside-text refused), the dotted-path clause with the
stored-mirror link preserved, a warn callout for the one-bad-entry blast radius, and
the
os validate/ runtime verdict table.Terminology deliberately mirrors
skills/objectstack-ui/SKILL.md→ "Toolbar Search(
searchableFields, ADR-0061)" (merged PR #6898, #6675) so the docs corpus and the skillscorpus say the same thing in the same words. Both intra-repo anchors were computed with
the repo's own
github-slugger@2.0.0rather than guessed(
#toolbar-search-searchablefields,#global-search--searchable--searchablefields), withthe two already-linked headings on the page used as controls.
On "reverse verification" — reported straight, not template-shaped
There is no direction in which restoring the old prose turns a test red: documentation
sentences are not asserted by anything in this repo, so manufacturing a before-red /
after-green artifact here would be a fabrication that reads as verification.
The evidence that carries this change is the opposite move and it is real: the old row's
claim, executed against the harness above, is falsified by direction A — the request
the row says returns
400 INVALID_FIELDreturns rows instead, and returns exactly the rowwhose term lives only in the lookup column.
Sibling pages: checked, all already correct
The card asked to check the pages landed by the same PR before editing. Each states the
set-membership rule already, so none needed a change and none is touched:
content/docs/data-modeling/schema-design.mdx:134,143— "If the object declares nosearchableFieldsat all…"; "searchableFieldsadmits any field the object declares".content/docs/api/data-api.mdx:27,144— "its declaredsearchableFields, or atext-like auto-default when none are declared"; the three-causes split.
content/docs/protocol/objectql/query-syntax.mdx:862andcontent/docs/data-modeling/queries.mdx:508— same phrasing.The type-first error was unique to
views.mdx:106.Gates (foreground, inside the shared verification lock)
check:skill-examplescovers this page's fouros:checkblocks(
content/docs/ui/views.mdx:16 / 172 / 404 / 438— the last three shifted by theinsertion); all four still type-check.
fumadocs-mdxregenerates the page cleanly, so thenew callout and tables parse. Control-byte self-scan beyond the gate:
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' content/docs/ui/views.mdx— no hits.Docs Drift Checkdoes not fire here: it triggers onpackages/**, and this PR touchesnone.
Scope — deliberately not done
skills/objectstack-ui/SKILL.mduntouched. It is the source of truth as of PRdocs(skills): objectstack-ui 补 searchableFields 章节 —— 工具栏搜索的收窄语义与失败边界 (#6675) #6898; this PR brings the docs page into agreement with it, never the reverse.
packages/lint/src/validate-searchable-fields.tsandpackages/metadata-protocol/src/protocol.tsuntouched — that is The searchable-fields hints prescribe a "text/formula" mirror — the formula half never works, so the tool output now contradicts the docs that quote it #6673 (the hintstrings that prescribe a formula mirror), dispatched in parallel. I read both while
measuring and have no correction to add to that card.
docs/adr/changes, nocontent/docs/releases/changes.content/docs/is a domain-boundary-contested path (域标签词表有两个未入表条目:domain:spec-tooling(在用,文件面与 devx 完全重叠)与domain:ui(一例,与repo:objectui重复) #5469, unruled — thedomain:spec-toolingseat also lands here). The diff is deliberately one file and onesection; nothing outside
content/docs/ui/views.mdxis touched.content/docs/-only prose, releasing no package —skip-changesetapplied by hand, following the precedent of PR docs(protocol): preserveAudit 的 readonly 白名单只在 UPDATE 半边成立 —— state-machine.mdx 的 create 半边旧说法 (#6827) #6880 (also docs-only, also labelled by
the authoring agent).
Generated by Claude Code