Skip to content

Commit cff17af

Browse files
os-warrenclaude
andauthored
fix(spec): drop dead 'status' member from SEARCHABLE_ENUM_TYPES (#13728)
'status' is not a member of the 49-value FieldType enum, so it could never match a real field type — dead vocabulary in search-fields.ts (verified mechanically: FieldType has 49 members, 'status' is absent, 'select' is present; no in-flight plan for a status field type found). Adds a [#13695] pin asserting SEARCHABLE_ENUM_TYPES stays a real FieldType subset, scoped to that one set — it closes the probe gap that let this sit unnoticed (the existing [#6934] pins check the search vocabularies against each other, never against FieldType itself). A parallel ghost-member finding in SEARCH_AUTO_EXCLUDED_TYPES is out of scope for this fix and filed separately. Includes a patch changeset for @objectstack/spec. _Generated by [Claude Code](https://claude.ai/code)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 1221712 commit cff17af

3 files changed

Lines changed: 53 additions & 1 deletion

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
fix(spec): drop the dead `'status'` member from `SEARCHABLE_ENUM_TYPES` (#13695)
6+
7+
`SEARCHABLE_ENUM_TYPES` in `packages/spec/src/data/search-fields.ts` declared
8+
`new Set(['select', 'status'])`, but `'status'` is not — and has never been —
9+
a member of the 49-value `FieldType` enum. The entry could never match a real
10+
field's `type`, so it changed no accept/reject behaviour and matched no field
11+
in any object: dead vocabulary that invited the next reader to believe a
12+
`status` field type exists.
13+
14+
Verified before removal (not read): `FieldType.options` has 49 members,
15+
`'status'` is absent, `'select'` is present; no in-flight plan for a `status`
16+
field type exists anywhere in the tree.
17+
18+
Grade: `patch`, argued rather than defaulted. Not `skip-changeset` — this
19+
touches published package source, not just docs/tests — and not a no-op
20+
either: this closes a real probe gap. The existing `[#6934]` pins in
21+
`search-fields.test.ts` check the four search vocabularies against **each
22+
other** (pairwise disjointness) but never against `FieldType` itself, so a
23+
pure ghost member — matching nothing, rather than overlapping something —
24+
passed every existing pin silently. This PR adds a `[#13695]` pin asserting
25+
`SEARCHABLE_ENUM_TYPES ⊆ FieldType`, scoped to that one set; a parallel ghost
26+
finding in `SEARCH_AUTO_EXCLUDED_TYPES` (`'object'`, `'grid'`, `'geometry'`,
27+
`'encrypted'` are also not `FieldType` members) is filed separately and left
28+
untouched here.

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
SEARCHABLE_ENUM_TYPES,
1212
SEARCHABLE_TEXTUAL_TYPES,
1313
} from './search-fields';
14+
import { FieldType } from './field.zod';
1415

1516
// ---------------------------------------------------------------------------
1617
// [#4483] The auto-default's "lead" field ORDERS the set; it must not ADMIT one.
@@ -142,6 +143,29 @@ describe('[#4483] $search auto field set — lead orders, never admits', () => {
142143
// two resolution assertions below go red on an overlap independently of them,
143144
// and in opposite directions, so no single relaxation can make this pin vacuous.
144145
// ---------------------------------------------------------------------------
146+
// ---------------------------------------------------------------------------
147+
// [#13695] `SEARCHABLE_ENUM_TYPES` members must be real `FieldType`s.
148+
//
149+
// The #6934 disjointness pins above check the vocabularies against EACH OTHER
150+
// but never against `FieldType` itself, so a member that matches no real field
151+
// type at all — a pure ghost, distinct from an overlap — passed every existing
152+
// pin silently: `'status'` sat in this set matching nothing, for as long as it
153+
// took a docs sweep to notice by hand. This pin closes that probe gap for the
154+
// enum vocabulary specifically (the one the finding hit); it is deliberately
155+
// NOT extended to `SEARCH_AUTO_EXCLUDED_TYPES`, which is a separate, larger
156+
// finding of its own (`object`/`grid`/`geometry`/`encrypted` are ghosts there
157+
// too) filed out of scope for this fix.
158+
// ---------------------------------------------------------------------------
159+
describe('[#13695] SEARCHABLE_ENUM_TYPES ⊆ FieldType', () => {
160+
const validTypes: ReadonlySet<string> = new Set(FieldType.options);
161+
162+
it('every member is a real FieldType — no ghost vocabulary entries', () => {
163+
for (const t of SEARCHABLE_ENUM_TYPES) {
164+
expect(validTypes.has(t), `'${t}' is in SEARCHABLE_ENUM_TYPES but not a FieldType member`).toBe(true);
165+
}
166+
});
167+
});
168+
145169
describe('[#6934] search type vocabularies are pairwise disjoint', () => {
146170
const overlap = (a: ReadonlySet<string>, b: ReadonlySet<string>) =>
147171
[...a].filter((t) => b.has(t)).sort();

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export const SEARCHABLE_TEXTUAL_TYPES: ReadonlySet<string> = new Set([
3939
'text', 'email', 'phone', 'url', 'autonumber', 'textarea', 'markdown',
4040
]);
4141
/** Enumerated types searched by mapping the query to option values via labels. */
42-
export const SEARCHABLE_ENUM_TYPES: ReadonlySet<string> = new Set(['select', 'status']);
42+
export const SEARCHABLE_ENUM_TYPES: ReadonlySet<string> = new Set(['select']);
4343
/** System / audit / heavy fields never auto-included. */
4444
export const SEARCH_AUTO_EXCLUDED_FIELDS: ReadonlySet<string> = new Set([
4545
'id', '_id', 'created', 'modified', 'created_at', 'updated_at',

0 commit comments

Comments
 (0)