Skip to content

docs(adr-0061): record ruling 6573569376 letter A — field-level searchable is honoured on the auto branch - #29756

Merged
os-zhuang merged 3 commits into
mainfrom
claude/project-thread-namumk
Nov 3, 2026
Merged

os-zhuang merged 3 commits into
mainfrom
claude/project-thread-namumk

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Warren · project thread
Fixes #29391

Clause-②: no
Metadata-protocol: none

维护者速读

What changes

One file, docs/adr/0061-record-search-architecture.md, +32 / -0:

  • Header. A new **Amended by**: line between **Builds on**: and **Consumers**:, the ADR-0005 shape: field.searchable: true has no reader: $search skips the field and $searchFields naming it answers 400 "is not searchable" while the served schema says searchable: true #29008, the ruling record, the landing PR and sha, one sentence on what is reversed, and a pointer to the note. The **Status**: line is untouched, so docs/adr/README.md (generated from that line) does not move.
  • D2. An indented amendment note directly under the last D2 bullet; the amended sentence itself is left as written. The note records the ruling and nothing else: the flag is honoured on the auto-default branch; the resolved set is the type-based auto-default plus every field marked searchable: true except secret / password / vector (packages/spec/src/data/search-fields.ts#SEARCH_FLAG_EXCLUDED_TYPES) and a virtual formula field; the flag is asked before the auto-default's own name / hidden / type exclusions (#admittedBySearchableFlag), so a flagged hidden or system-named field joins and an unreadable type does not; a declared searchableFields is the whole set and the flag is not read there (#resolveSearchFieldResolution); the $searchFields ingress allow-list is the same resolution (packages/metadata-protocol/src/protocol-query-validation.ts#assertSearchFieldsAreSearchable), so a flagged field is accepted as a D1 override and a flagged hard-excluded type is still refused 400 INVALID_FIELD; a field declared internal: true (security(data): the data door's filter and group-by positions do not honour a field's internal: true the way its row read does — detail withheld pending maintainer #22646, ADR-0100's third credential channel, an auth-subsystem one-way hash on a text column) is refused at that same door before the resolver runs, flag or no flag; false removes nothing. It then gives the reconciled reading of the "Default behaviour (normative)" field-resolution row (auto arm widened the same way; row left as written) and the D5 re-read.
  • D5 re-read (inside the note). The sentence "Secret / encrypted / PII fields are never searchable (excluded from default, rejected from override)" stays true under the flag, through two pre-existing mechanisms the flag does not reach. For the two masked-on-read credential types, the hard exclusions: SEARCH_FLAG_EXCLUDED_TYPES covers packages/spec/src/data/masked-field-types.ts#MASKED_ON_READ_FIELD_TYPES (secret, password; encrypted is not a FieldType), so such a column marked searchable: true is neither scanned nor accepted as an override. For the third channel, an internal: true column, the pre-existing withhold (security(data): the data door's filter and group-by positions do not honour a field's internal: true the way its row read does — detail withheld pending maintainer #22646): the engine strips internal: true and credential fields from the field map before it resolves the set (scanFields in packages/objectql/src/engine.ts#expandSearchOnAst), and the door refuses an explicit override naming one before the resolver runs (assertSearchFieldsAreSearchable). The resolver reads no PII marker before or after the ruling, and the flag changes which fields resolve, not how a search executes, so the rest of D5 (one gated find() pipeline) is untouched.
  • Default behaviour table. A one-line blockquote under the table pointing at the D2 note, linking the card and the ruling record the way the header does; the row is not rewritten.

Why

Ruling record 6573569376 on #29008 (letter A, the maintainer's 「同意」 2026-11-02T09:22Z) chose "honour field.searchable, not retire it", and PR #29385 (merged 2026-11-02, 29c70cac) landed it. ADR-0061 D2 still said the opposite. The card (#29391) is the governed follow-up PR #29385's contract review named; docs/adr/** is Tier H, so it could not ride that PR. The drafted note the card cites was not in project files, so this note is redrafted from the ruling record and the merged resolver, as the dispatch order asks.

Scope: wording that records the ruling. No new decision and no superseding ADR; nothing outside the one file.

Tests

n/a. A docs-only edit under docs/adr/**: no package is touched, so no package test or typecheck is owed, and nothing publishes (fast lane, skip-changeset). Verification is the derived gate set below; every claim in the note was read against the merged code on origin/main c76c0e04 (search-fields.ts, its test's "field.searchable: true joins the auto-default set" and "SEARCH_FLAG_EXCLUDED_TYPES" describes, protocol-query-validation.ts (including the #22646 internal: true refusal that runs ahead of resolveSearchFieldResolution), packages/objectql/src/engine.ts (expandSearchOnAst: the scanFields strip built from collectInternalReadFields and collectCredentialFields), masked-field-types.ts, field.zod.ts describe).

Acceptance notes

  • Shape choices for the approver. The reconciliation of the "Default behaviour (normative)" row lives in the D2 note, plus a one-line pointer under the table so a reader who lands on the table is not left with the stale auto arm; the row is left as written. Strike the pointer if one note is preferred. The header entry is on its own **Amended by**: line, not on **Status**:, as the order asks.
  • Pre-existing drift, not touched (outside this order). The D2 "Resolution order" bullet lists the short-text set as text / email / phone / url; today SEARCHABLE_TEXTUAL_TYPES also has autonumber, textarea, markdown, and SEARCHABLE_ENUM_TYPES has select. The amendment note says "type-based auto-default" and does not enumerate, so it stays true. Read-only inference, not a card; carrier: none.
  • D5's word "PII". No field-level PII marker exists in FieldSchema and the resolver reads none; the note states this as a reading, not a decision. Not a card (no declared mechanism is violated: D5's parenthetical names type exclusion, which holds); carrier: none.
  • Commit identity. The one commit is authored objectstack-fleet[bot] with the model-free trailer pair; the harness's own attribution reminder names a model in its Co-Authored-By line, which the pre-push hook refuses, so the AGENTS.md form was used. Reported, not a deviation.
  • Review rounds. The seat's contract review passed (6594883886) with should-fixes S1 (name the internal: true withhold in the ingress sentence and the D5 re-read) and S2 (link field.searchable: true has no reader: $search skips the field and $searchFields naming it answers 400 "is not searchable" while the served schema says searchable: true #29008 in the table pointer), landed as c520802; the delta review added S3 (anchor the engine withhold on the method expandSearchOnAst, not its local const), landed as 0d487e0; the delta review on 0d487e0 passed (6595091192) with no findings left.
  • Reviewers. The dev requested none, and the relay's pr_create summons nobody (its governed-review follow-up runs only at pr_ready); the Tier H approvers are asked at the seat's hand-off.

Verification

  • node scripts/pm/dispatch-gates.mjs --commands on this diff (merge base c76c0e04): 21 commands, the same list on heads 4d9ec80e and c5208020 (one file changed throughout). All 21 run on each, every exit 0; --ran reconciled on each: 21 derived, 21 run, 0 NOT-MEASURED, 0 UNRUN. On the final head 0d487e0e (one line, an anchor re-spelled) the three ADR gates below were rerun.
  • Prerequisite build for check:doc-formula-expressions: @objectstack/formula + @objectstack/lint under the verify lock, VERDICT command-exit 0, held 90 s.
  • ADR gates, the ones this edit reaches: check:adr-symbol-anchors — 3050 anchors across 149 records resolve (six new symbol anchors from this note, all declaration); check:adr-links — 979 relative links resolve; check:adr-anchors — exit 0; check:adr-index — docs/adr/README.md is current (Status line untouched).
  • Also exit 0: check:doc-authoring, check:nul-bytes, check:pm-governed-merges, check:pm-prior-rulings, check:closed-ceremony-text, check:cross-package-test-inputs, check:ci-filter-parity, check:driver-memory-census, check:gitlink-declared, check:refd-timer-probe, check:watch-hint-literal, check-closing-keyword-parity (+ self-test), check-comment-mask-corpus, plan-test-core-shards --self-test, check-adr-links --self-test.
  • NOT MEASURED locally, CI-owned: the type-check lanes, the Plan Test Core shards step, the 11 whole-root families and the artifact roster; this PR's check-runs are the reading for those.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (No schema touched.)
  • 2. Every name I added uses the casing for its kind. (No names added.)
  • 3. No formatter ran; pnpm lint passes. (No formatter; the repo-wide eslint run is CI's, and no lintable file is touched.)
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. (Nothing publishes: docs/adr/** fast lane, skip-changeset; both declaration lines are above.)
  • 6. packages/spec touched: its generated artifacts are regenerated and committed. (Not touched.)
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English. (The 维护者速读 block is the os-dev maintainer brief, in Chinese by that file's rule.)
  • The Before Submitting list in CONTRIBUTING.md is done.

…hable is honoured on the auto branch

An amendment note directly under the D2 bullet (the amended sentence is
left as written), a one-line pointer under the "Default behaviour
(normative)" table, the D5 re-read, and an **Amended by** header entry
in the ADR-0005 shape. Every claim cites the merged resolver by
file#symbol; the recorded decision is the ruling's, nothing new.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQwxbY1n95XjHLy7atRLWP
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Nov 3, 2026
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation labels Nov 3, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-warren · session_01JQwxbYyp6wSRcywGFK2L4A

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 4d9ec80e3347db24741ea823c7599d40445da5f7
Local-runs: none
Scope: docs/adr/0061-record-search-architecture.md + packages/spec/src/data/search-fields.ts, packages/metadata-protocol/src/protocol-query-validation.ts, packages/spec/src/data/masked-field-types.ts, packages/spec/src/data/field.zod.ts, packages/objectql/src/engine.ts, packages/objectql/src/search-filter.ts, packages/spec/src/data/search-fields.test.ts, docs/adr/0005-metadata-customization-overlay.md

Reviewer: an at-tier subagent adopted by this seat. Inputs: card #29391 and every comment, ruling record 6573569376 and the #29008 thread, merged PR #29385, PR #29756's body, file list and net diff (one file, +26/-0, merge base c76c0e04), the head's check-runs (24 success, none failed; four type-check/Test Core runs unreachable from a docs-only diff still in progress at review time, read again by the seat before handoff).

① Derived judgments

Each claim the new text makes, read against origin/main c76c0e04:

  1. Auto branch = type-based default plus every searchable: true field except secret/password/vector and a virtual formula: right (admittedBySearchableFlag, SEARCH_FLAG_EXCLUDED_TYPES, SEARCH_VIRTUAL_TYPES, pinned in search-fields.test.ts).
  2. The flag is asked before the default's name/hidden/type exclusions; an unreadable type is not admitted: right (order in autoDefaultFields; both pinned).
  3. A declared searchableFields is the whole set, flag not read there: right (resolveSearchFieldResolution declared branch).
  4. $searchFields ingress is the same resolution; a flagged hard-excluded type is still refused 400 INVALID_FIELD: right (assertSearchFieldsAreSearchable), with a pre-existing internal: true refusal ahead of the resolver the note does not name (④ S1).
  5. The key defaults to false, and false removes nothing: right (field.zod.ts; resolver tests === true).
  6. encrypted is not a FieldType; MASKED_ON_READ_FIELD_TYPES = secret, password ⊂ SEARCH_FLAG_EXCLUDED_TYPES: right (encrypted/pii exist only as pruned authoring keys refused at lint).
  7. The flag changes resolution, not execution (still the gated find() pipeline): right.

② Semver level

skip-changeset right: one docs/adr/** file, nothing publishes. Clause-②: no, Metadata-protocol: none right for a docs-only diff; the widening itself was declared and reviewed on PR #29385. Check Changeset and PR-body guards green.

③ Boundary flags

The dev's offer to strike the one-line pointer under the "Default behaviour (normative)" table: keep it. That table is the record's only normative part and its auto arm is now narrower than the resolver; one line, nothing rewritten, same placement precedent as the 2026-10-01 note under D5. Nothing outside docs/adr/0061-* is touched; claim 6594523070 is the branch's identity record.

④ Design judgment

Against the first consumer (the merged resolver, the ingress gate, the engine expansion) and the ruling's execution plan:

  • Every sentence of the amendment note holds (① 1–7); attempts to break each (flagged code/html/lookup/json, each hard-excluded type, flagged formula, declared set, absent vs false) found no contradiction.
  • The note sits under D2 as an indented blockquote; "Field-level searchable stays pruned." is byte-identical to main.
  • **Amended by**: follows ADR-0005's shape on its own line; the **Status**: line is unchanged, so the ADR index does not move (check:adr-index green).
  • No new decision beyond ruling letter A: the virtual-formula exclusion and the flag-first order are the landed implementation reviewed on PR feat(spec): a field marked searchable: true is searched when its object declares no searchableFields #29385 (records 6579041743, 6580195493), recorded with file#symbol anchors that resolve.
  • D5 re-read holds: no encrypted key and no PII marker exist on FieldSchema. ADR-0100's third credential channel (a hashed text column declared internal: true) is not covered by SEARCH_FLAG_EXCLUDED_TYPES, and admittedBySearchableFlag reads no internal; it stays never-searchable because the engine strips internal/credential fields before resolving (engine.ts scanFields) and the ingress refuses an internal name first (security(data): the data door's filter and group-by positions do not honour a field's internal: true the way its row read does — detail withheld pending maintainer #22646). D5 holds at both faces, but by that withhold, not by "the hard exclusions".

Findings:

No must-fix.

Implemented-by: claude/project-thread-namumk
Reviewed-by: session_01JQwxbYyp6wSRcywGFK2L4A

VERDICT: PASS

…ink the table pointer

Seat review should-fixes. The ingress sentence now says an `internal: true`
field (ADR-0100's third credential channel, an auth-subsystem one-way hash
on a text column) is refused at the data door before the resolver runs, and
the D5 re-read says that channel holds by the pre-existing withhold (the
engine strips internal and credential fields from the field map before it
resolves the set) rather than by the flag's hard exclusions. Landed
behaviour recorded with file#symbol anchors; no new decision. The table
pointer links the card and the ruling record the way the header does.

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

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-warren · session_01JQwxbYyp6wSRcywGFK2L4A

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: c52080208b4d28e745c08d1e538a6c2dae28d056
Local-runs: none
Scope: docs/adr/0061-record-search-architecture.md (delta 4d9ec80e..c5208020, +10/-4, fast-forward) + packages/metadata-protocol/src/protocol-query-validation.ts, packages/objectql/src/engine.ts, packages/spec/src/data/field.zod.ts, scripts/symbol-anchors.mjs

Delta review by the same at-tier reviewer as record 6594883886, answering its S1 and S2. Cited code read on origin/main c76c0e04.

① Derived judgments

  1. An internal: true field is refused at the $searchFields door before the resolver runs, flag or no flag: right (assertSearchFieldsAreSearchable throws 400 INVALID_FIELD on collectInternalWriteResponseFields before calling resolveSearchFieldResolution; it reads no searchable).
  2. The engine strips internal: true and credential fields from the field map before resolving: right (expandSearchOnAst builds scanFields = fields minus collectInternalReadFields ∪ collectCredentialFields, then resolves on scanFields).
  3. ADR-0100's third credential channel is an auth-subsystem one-way hash on a text column: right (field.zod.ts internal describe and TSDoc).

② Semver level

Unchanged: docs-only docs/adr/**, skip-changeset, Clause-②: no, Metadata-protocol: none.

③ Boundary flags

S2 closed: the table pointer links #29008 and 6573569376 byte-identically to the header and the D2 note. The pointer stays.

④ Design judgment

No must-fix.

Implemented-by: claude/project-thread-namumk
Reviewed-by: session_01JQwxbYyp6wSRcywGFK2L4A

VERDICT: PASS

…its local const

Seat review S3. The symbol anchor for the engine's internal/credential strip
now names the method, the way other ADRs anchor engine.ts; the prose keeps
the scanFields name. No wording change otherwise.

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

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-warren · session_01JQwxbYyp6wSRcywGFK2L4A

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 0d487e0e0668f189363f3713b56dab7fd67755ea
Local-runs: none
Scope: docs/adr/0061-record-search-architecture.md (delta c5208020..0d487e0e, +1/-1, fast-forward) + packages/objectql/src/engine.ts, scripts/symbol-anchors.mjs

Delta review by the same at-tier reviewer, answering S3 of record 6595041951. Net PR diff against merge base c76c0e04 is still one file.

① Derived judgments

No behavioural claim changes. The sentence now anchors scanFields in packages/objectql/src/engine.ts#expandSearchOnAst: right, that method builds scanFields and resolves on it.

② Semver level

Unchanged: docs-only docs/adr/**, skip-changeset, Clause-②: no, Metadata-protocol: none.

③ Boundary flags

None. Table pointer, **Amended by**: entry, **Status**: line and the amended D2 sentence are byte-identical to c5208020.

④ Design judgment

The note's six symbol anchors now name declarations: search-fields.ts#SEARCH_FLAG_EXCLUDED_TYPES, #admittedBySearchableFlag, #resolveSearchFieldResolution, protocol-query-validation.ts#assertSearchFieldsAreSearchable, masked-field-types.ts#MASKED_ON_READ_FIELD_TYPES, engine.ts#expandSearchOnAst (resolves under scripts/symbol-anchors.mjs's member-at-line-start rule). scanFields survives as prose only. S3 closed; no new decision. No findings.

Implemented-by: claude/project-thread-namumk
Reviewed-by: session_01JQwxbYyp6wSRcywGFK2L4A

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-warren · session_01JQwxbYyp6wSRcywGFK2L4A

维护者速读

  • 改了什么:只改 docs/adr/0061-record-search-architecture.md(+32 行,无删改)。D2「Field-level searchable stays pruned」原句不动,其下加修订注,记录 field.searchable: true has no reader: $search skips the field and $searchFields naming it answers 400 "is not searchable" while the served schema says searchable: true #29008 裁决 A:未声明 searchableFields 时,标了 searchable: true 的字段进入自动默认集(secret/password/vector 与虚拟 formula 除外);声明了 searchableFields 则不读开关;$searchFields 入口同一套解析;既有的 internal: true 截留照记,开关绕不过。表下一行指针;复读 D5;头部加 **Amended by**:,Status 行不动。
  • 为什么改:PR feat(spec): a field marked searchable: true is searched when its object declares no searchableFields #29385 落地后 D2 这句与代码相反;Prime Directive 13 要求把反转记在 ADR 上。
  • 风险与回滚:纯文字,不发布、不改行为、不新作决定;回滚即 revert。
  • 先看:D2 下的修订注(对照 search-fields.ts#admittedBySearchableFlag、#SEARCH_FLAG_EXCLUDED_TYPES);注末 D5 复读两句;头部 **Amended by**: 行。
  • 两分钟验证:pnpm check:adr-symbol-anchors,预期 3050 个锚全部解析(本 PR 新增 6 个,均为声明锚)。
  • 需要你做的:Tier H(docs/adr/**):请 GOVERNED_APPROVERS 之一批准;批准后席位落地。
  • 席位意见:建议批准。fable 达档复核三轮(PASS 6594883886 → delta 6595041951 → delta 6595091192),三条 should-fix 全部落在本 head;注文只记录已落地行为,不新作决定。按你的常设档位令以 fable + xhigh 派发。

席位核对

ACCEPT · domain:server#3 · head 0d487e0e0668f189363f3713b56dab7fd67755ea · 2026-11-03T04:09Z · governed docs/adr/** (Tier H: check-governed-merges --pr 29756 1 of 1 path governed, 32 lines); CONTRACT_REVIEW_TIER review PASS on this head (6595091192, delta of 6594883886 / 6595041951); final four-piece hand-off to the approvers.

  1. Shape: draft, base main, Fixes #29391, Clause-②: no, Metadata-protocol: none, skip-changeset (docs-only fast lane). 1 file, +32/−0. PR body refreshed to this head by the seat (relay issue_patch, read-back identical).
  2. Premise measured: on origin/main c76c0e04, D2 still said the flag stays pruned while admittedBySearchableFlag honours it (PR feat(spec): a field marked searchable: true is searched when its object declares no searchableFields #29385, 29c70cac). Every sentence of the note was read against the merged resolver, the ingress gate and the engine by the at-tier reviewer.
  3. Gates on this head: check-runs pinned to 0d487e0e: 45 total, 26 success, 19 skipped, 0 failed (Lint & Repo Gates, TypeScript Type Check and Lint · docs & code guards, which runs check:adr-symbol-anchors, all success); mergeable_state clean. Dev: 21/21 derived gates exit 0 on 4d9ec80e and c5208020, the three ADR gates rerun on 0d487e0e (3050 anchors, 979 links, index current).
  4. Prose checked against the diff: the ADR text is the reviewed face (records above). The PR body's D5 bullet ("through two pre-existing mechanisms the flag does not reach") matches the note on this head.
  5. Out of scope: D2's "Resolution order" bullet enumerates fewer short-text types than SEARCHABLE_TEXTUAL_TYPES (pre-existing; the note avoids enumerating). No PII marker exists in FieldSchema (a reading, not a decision). Neither earns a card. Skills-docs: not owed.
  6. Landing: Tier H ⇒ relay pr_request_reviewers to GOVERNED_APPROVERS (os-zhuang, hotlong) and needs-user-decision on the PR in the same act; stays draft. On an approver's APPROVED: clear the label, pr_ready + automerge_enable, follow to MERGED, landing record on docs(adr-0061): record that field-level searchable is honoured on the auto branch (D2 says it stays pruned; ruling 6573569376 letter A reversed it in #29008) #29391.

@os-zhuang
os-zhuang marked this pull request as ready for review November 3, 2026 04:11
@os-zhuang
os-zhuang added this pull request to the merge queue Nov 3, 2026
Merged via the queue into main with commit 2c91a55 Nov 3, 2026
51 checks passed
@os-zhuang
os-zhuang deleted the claude/project-thread-namumk branch November 3, 2026 04:24
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

1 participant