Skip to content

Commit 146115a

Browse files
test(spec): pin SEARCH_AUTO_EXCLUDED_TYPES to every credential type in MASKED_ON_READ_FIELD_TYPES (#28123)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} --> _Requested by **Tesla** · [project thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6JaoD992ixKFWg6AWFdAVqMbwG)_ Fixes #27822 Clause-②: no Metadata-protocol: none ## 维护者速读 - 改了什么:在 `search-fields.test.ts` 加一条断言:`MASKED_ON_READ_FIELD_TYPES` 里的每个凭据类型(今天是 `secret`、`password`)都必须在 `SEARCH_AUTO_EXCLUDED_TYPES` 里。只改测试,不改任何发布代码。 - 为什么改:引擎、lint 判凭据类型读的是掩码表,而 REST 入口的 `$searchFields` 默认集读的是手写的排除集。以后有人往掩码表加新凭据类型却忘了排除集,REST 会放行引擎会扣下的字段;这条断言让那次修改当场变红。 - 风险与回滚:零运行时风险,发布值不变;回滚即 revert 本 PR。 - 先看:`packages/spec/src/data/search-fields.test.ts` 新增的 `SEARCH_AUTO_EXCLUDED_TYPES covers every credential type` 块。 - 两分钟验证:`pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/data/search-fields.test.ts`,预期 33/33 通过。 - 需要你做的:无,席位落地。 ## What changes - One new `describe` in `packages/spec/src/data/search-fields.test.ts`: for every own key of `MASKED_ON_READ_FIELD_TYPES`, assert `SEARCH_AUTO_EXCLUDED_TYPES.has(key)` (one `it.each` row per credential type, with a message naming the type), plus a CONTROL that the table is non-empty so the pin cannot pass vacuously. - Route: the pin, not the derivation. Deriving the set's credential members from the table would edit shipped source (`search-fields.ts`) and need a patch changeset; the triage record asks for a single assertion, and the pin closes the drift at the same edit. ## Tests - Red first: injected a scratch key `textarea` into a local copy of `MASKED_ON_READ_FIELD_TYPES` (not committed; restored with `git checkout HEAD --`, hash matched the HEAD blob). `search-fields.test.ts` → `× credential type textarea is auto-excluded`, `Tests 1 failed | 32 passed (33)`, exit 1. - Green at `799bb76f`: `search-fields.test.ts` + `masked-field-types.test.ts` → `Test Files 2 passed`, `Tests 43 passed`. - `pnpm --filter @objectstack/spec test && pnpm --filter @objectstack/spec typecheck` under the verify lock → `VERDICT command-exit 0`; `Test Files 755 passed | 1 skipped (756)`, `Tests 22511 passed | 1 skipped | 1 todo`. ## Acceptance notes - `skip-changeset`: the diff is a test file only. `@objectstack/spec`'s `files[]` ships `dist` and `src/**/*.zod.ts`; after a build the new test's describe string has 0 hits in `dist/`, while the positive control `SEARCH_AUTO_EXCLUDED_TYPES` hits `dist/browser/data/index.js`. - The REST ingress itself (`assertSearchFieldsAreSearchable`) is unchanged; it still reads `SEARCH_AUTO_EXCLUDED_TYPES`, which this pin now holds to the credential vocabulary. ## Verification <details><summary>Derived gates at 799bb76 (73 families)</summary> - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` → 73 commands; every line run with stdin from /dev/null, exit codes recorded before any pipe. - 72 exit 0. Three first answered exit 3 (PREREQUISITE NOT MET) and were re-run after their prerequisite: `check-plugin-teardown-shape --self-test` (after `git fetch --unshallow`), `@objectstack/lint check:doc-formula-expressions` and `check:lean-entry-closure` (after building the lint and objectql closures) → exit 0. - NOT MEASURED: `pnpm check:dual-build-cjs-loads`, reason: needs every workspace package built (89+ missing `dist/`); CI's `Lint & Repo Gates` runs it. - `dispatch-gates.mjs --repo objectstack-ai/objectstack --ran` → `73 derived famil(ies) accounted for — 72 run, 1 NOT-MEASURED`. </details> 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01L9qibzWK3xztCU4oKFvCGo --- _Generated by [Claude Code](https://claude.ai/code/session_01L9qibzWK3xztCU4oKFvCGo)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 9324232 commit 146115a

1 file changed

Lines changed: 29 additions & 0 deletions

File tree

‎packages/spec/src/data/search-fields.test.ts‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
SEARCHABLE_TEXTUAL_TYPES,
1313
} from './search-fields';
1414
import { FieldType } from './field.zod';
15+
import { MASKED_ON_READ_FIELD_TYPES } from './masked-field-types';
1516

1617
// ---------------------------------------------------------------------------
1718
// [#4483] The auto-default's "lead" field ORDERS the set; it must not ADMIT one.
@@ -239,6 +240,34 @@ describe('search type vocabularies are pairwise disjoint', () => {
239240
});
240241
});
241242

243+
// ---------------------------------------------------------------------------
244+
// [#27822] Every credential type is auto-excluded from search.
245+
//
246+
// `MASKED_ON_READ_FIELD_TYPES` is the one credential vocabulary: the engine's
247+
// search withhold, the aggregate refusal, the evaluation guard and the lint
248+
// check all read its keys (`isCredentialFieldType`). The REST ingress
249+
// (`assertSearchFieldsAreSearchable`) does not — it resolves the auto-default
250+
// over the unfiltered field map with `SEARCH_AUTO_EXCLUDED_TYPES`, which names
251+
// its credential members by hand. A type added to the mask table and not to
252+
// this set would be withheld by the engine yet admitted at the REST door in an
253+
// auto-default `$searchFields`. This pin turns that edit red here instead.
254+
// ---------------------------------------------------------------------------
255+
describe('SEARCH_AUTO_EXCLUDED_TYPES covers every credential type', () => {
256+
const credentialTypes = Object.keys(MASKED_ON_READ_FIELD_TYPES);
257+
258+
it.each(credentialTypes)('credential type %s is auto-excluded', (type) => {
259+
expect(
260+
SEARCH_AUTO_EXCLUDED_TYPES.has(type),
261+
`'${type}' is masked on read but the search auto-default would scan it`,
262+
).toBe(true);
263+
});
264+
265+
it('CONTROL — the mask table is not empty', () => {
266+
// An emptied table would green the per-type pin above while pinning nothing.
267+
expect(credentialTypes.length).toBeGreaterThan(0);
268+
});
269+
});
270+
242271
// ---------------------------------------------------------------------------
243272
// [#6674] A VIRTUAL field declared in `searchableFields` is not admitted.
244273
//

0 commit comments

Comments
 (0)