Repository navigation
Commit 2c91a55
docs(adr-0061): record ruling 6573569376 letter A — field-level searchable is honoured on the auto branch (#29756)
<!-- claude-projects-attribution: {"github_login":"os-warren"} -->
_Requested by **Warren** · [project
thread](https://claude.ai/code/project/chan_01U5RdnAAmkSQmZbqh6FtqZ8?thread=cmsg_01U5RdnAAmkSQmZbqh6FtqZ8SYDKd2MYzvNu6rLT1Fwx94)_
Fixes #29391
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:只改 `docs/adr/0061-record-search-architecture.md`(+32 行,无删改)。D2
末条「Field-level `searchable` stays pruned」原句不动,其下加一段修订注,记录 #29008 裁决记录
6573569376 的 A 案:对象未声明 `searchableFields` 时,标了 `searchable: true`
的字段进入自动默认集,`secret` / `password` / `vector` 与虚拟 `formula` 字段除外;声明了
`searchableFields` 的对象不读该开关;`$searchFields` 入口放行集同一套解析;既有的 `internal:
true` 截留(#22646,ADR-0100 第三条凭据通道)照记:入口在解析前即拒,引擎在解析前把 internal
与凭据字段从字段表剔除,开关绕不过它。「Default behaviour
(normative)」表下加一行指针(卡号与裁决记录带链接),表行不改。同注内复读 D5。头部加 `**Amended by**:`
条目(ADR-0005 形制),Status 行不动。
- 为什么改:PR #29385(`29c70cac`)落地后,D2 这句与代码相反;Prime Directive 13 要求把反转记在
ADR 上。注文每一句都对照 `origin/main` `c76c0e04` 的
`packages/spec/src/data/search-fields.ts` 与入口门禁写,并以 file#symbol 引用。
- 风险与回滚:纯文字,不发布、不改行为、不新作决定;回滚即 revert。
- 先看:① D2 下的修订注(逐句对照 `search-fields.ts#admittedBySearchableFlag` 与
`#SEARCH_FLAG_EXCLUDED_TYPES`);② 注末「D5 re-read」两句(硬排除 + `internal: true`
截留);③ 头部 `**Amended by**:` 行。
- 两分钟验证:`pnpm check:adr-symbol-anchors`,预期 3050 anchors 全部 resolve(本 PR
新增 6 个符号锚);或打开 `search-fields.ts` 的
`admittedBySearchableFlag`,对照注文第二、三句。
- 需要你做的:Tier H(`docs/adr/**`)——`GOVERNED_APPROVERS` 批准后由席位落地。
## 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: #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` (#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 (#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 #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
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(No schema touched.)
- [x] 2. Every name I added uses the casing for its kind. (No names
added.)
- [x] 3. No formatter ran; `pnpm lint` passes. (No formatter; the
repo-wide eslint run is CI's, and no lintable file is touched.)
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 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.)
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed. (Not touched.)
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English. (The `维护者速读` block is
the os-dev maintainer brief, in Chinese by that file's rule.)
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
<!-- signed: domain:server#3 · os-warren ·
session_01JQwxbYyp6wSRcywGFK2L4A -->
---------
Co-authored-by: objectstack-fleet[bot] <objectstack-fleet[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>1 parent 77ce3d4 commit 2c91a55
1 file changed
Lines changed: 32 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| 6 | + | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
| |||
41 | 42 | | |
42 | 43 | | |
43 | 44 | | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
44 | 74 | | |
45 | 75 | | |
46 | 76 | | |
| |||
72 | 102 | | |
73 | 103 | | |
74 | 104 | | |
| 105 | + | |
| 106 | + | |
75 | 107 | | |
76 | 108 | | |
77 | 109 | | |
| |||
0 commit comments