Skip to content

Commit 969ffba

Browse files
test(spec): the third data/ file group's test titles state each cited decision in words instead of a tracker number (stage 17) (#21856)
Part of #20749 Clause-②: no Stage 17 of this card: the next area of class (e), the test strings shipped under `packages/spec/src`, as ruled in `5902360492` on #20513. This stage takes the third name-ordered file group directly under `packages/spec/src/data/`: the 20 test files from `filter-comparand-type.test.ts` to `filter-view-operator-parity.test.ts`. They carried 95 messages and 99 tracker ids, citing 36 records. Every one of those ids now either states what its record decided, in words (form D), or is dropped where the title already says it. Text only: no assertion, identifier, test count or code comment changes. ## Census at the base (`2799155678`) Instruments: `census10.cjs` (md5 `9d08602ab972b4b8643c90d64d40fa41`), `census.cjs` (md5 `6e42a45a926d375013c32d62f16a296e`), `census-wide.cjs` (md5 `c98410a19529c439adb0afbfb00026a2`) and `dirtable.cjs` (md5 `dda605c54745b4a60cc14c9a686e4eff`). They are byte-identical to the copies stages 10 to 16 used. A literal counts as a test title when its folded message is argument 0 of a `describe` / `it` / `test` call, `.each` / `.skip` / `.only` chains included. Everything else is an "other" string. Both instruments read **1140 messages / 1207 ids in 245 files**, the seat's reading at `2799155678`. That is stage 16's head reading (1137 / 1204) plus the three ids that PRs #21819 and #21825 added in new `ui/` test titles, which ride the `ui/` stages. | directory | files | messages / ids | titles | other | |:--|--:|--:|--:|--:| | `ui/` | 84 | 396 / 419 | 378 / 401 | 18 / 18 | | `data/` (this PR: the third 20 files) | 55 | 280 / 299 | 265 / 282 | 15 / 17 | | `api/` | 40 | 189 / 201 | 181 / 193 | 8 / 8 | | `system/` | 34 | 154 / 165 | 128 / 138 | 26 / 27 | | (files directly in `src/`) | 30 | 118 / 120 | 117 / 119 | 1 / 1 | | `ai/` | 1 | 2 / 2 | 0 | 2 / 2 | | `contracts/` | 1 | 1 / 1 | 0 | 1 / 1 | | **total** | **245** | **1140 / 1207** | **1069 / 1133** | **71 / 74** | The group reads **95 messages / 99 ids in 20 files**, the seat's figures, file for file: | file (under `data/`) | messages / ids | titles | other | |:--|--:|--:|--:| | `filter-comparand-type.test.ts` | 9 / 10 | 9 / 10 | 0 | | `filter-cross-field-comparison-class.test.ts` | 4 / 4 | 4 / 4 | 0 | | `filter-dotted-head.test.ts` | 2 / 2 | 2 / 2 | 0 | | `filter-empty-operator.test.ts` | 5 / 5 | 5 / 5 | 0 | | `filter-equality-array-schema-door.test.ts` | 5 / 5 | 5 / 5 | 0 | | `filter-field-reference-lowering.test.ts` | 1 / 1 | 1 / 1 | 0 | | `filter-icontains-parse-door.test.ts` | 3 / 3 | 3 / 3 | 0 | | `filter-like-code-point.test.ts` | 3 / 3 | 3 / 3 | 0 | | `filter-like-nul-pattern.test.ts` | 1 / 1 | 1 / 1 | 0 | | `filter-like-wire-lowering.test.ts` | 3 / 3 | 3 / 3 | 0 | | `filter-lowering.test.ts` | 6 / 6 | 1 / 1 | 5 / 5 | | `filter-ne-array-schema-door.test.ts` | 5 / 6 | 5 / 6 | 0 | | `filter-number-comparand-declared-type.test.ts` | 17 / 17 | 17 / 17 | 0 | | `filter-operator-vocabulary.test.ts` | 2 / 4 | 1 / 1 | 1 / 3 | | `filter-preset-comparand.test.ts` | 2 / 2 | 2 / 2 | 0 | | `filter-save-door-face-parity.test.ts` | 14 / 14 | 7 / 7 | 7 / 7 | | `filter-text-comparand.test.ts` | 3 / 3 | 3 / 3 | 0 | | `filter-text-conformance.test.ts` | 2 / 2 | 1 / 1 | 1 / 1 | | `filter-text-operator-declared-type.test.ts` | 6 / 6 | 6 / 6 | 0 | | `filter-view-operator-parity.test.ts` | 2 / 2 | 2 / 2 | 0 | | **20 files** | **95 / 99** | **81 / 83** | **14 / 16** | Two more test files sit in the same name range and carry no id: `filter-subtree-provenance.test.ts` and `filter-verdict.test.ts`. They are not touched. - **Controls.** Lit, a title: `data/filter.test.ts`, outside the group, reads 18 / 19 at the head as at the base. Lit, "other" strings: the twin expect messages in `system/message-queue-retirement.test.ts` (`:87`, `:127`) still read at the head. Dark: `data/filter-operator-vocabulary.test.ts` reads 0 / 0 at the head while 11 of its comment lines still carry a number. Planted in a scratch copy of the head `data/filter-like-nul-pattern.test.ts`: an id put into a title reads 1 / 1, and an id put into a comment reads 0. - **A wider pattern** (any `#` plus digits) reads the same totals as the gate pattern in all 20 files at the base, and 0 in all 20 at the head. - **At the head:** 1045 messages / 1108 ids in 225 files. The 20 files read 0 / 0, and no other file moved. ## How the area was chosen `data/` has no subdirectory to split by, so its stages take name-ordered file groups near the ~100-id bound. Stage 16's re-cut named this group at 99, and this census reads 99, so the rule needed no re-cut. **Named for the next stages** (re-cut from the head census, 1045 / 1108; `data/` 185 / 200 left, the same cut stages 15 and 16 named): - `data/` in two more stages, name-ordered: 1. `filter.test.ts` to `object.test.ts`: 17 files, 103 messages / 114 ids. `object.test.ts` alone carries 42, so no cut lands nearer the bound; 2. `query-transport.test.ts` to `validation.test.ts` (11 files, 34 / 34) with `data/driver/` (7 files, 48 / 52): 86 ids. - `ui/` 419, about four stages. `api/` 201, two. `system/` 165, two. The files directly in `src/`, 120, one. - The three docblock needles (`ai/build-progress.test.ts:236`, `:237`, `contracts/approval-service.test.ts:274`), one stage with their docblocks. ## What each id became 20 literals (23 ids) now state a decision in words. 3 literals (4 ids) get their subject back in words where the number stood in for it. 72 literals (72 ids) drop a number the title already explains. Every cited record was read with its comments through REST: 35 answer 200. #14426 answers 404, and its decision was read from what landed: commit `40a44b91b` and its CHANGELOG entry (the `undefined` comparand refusal names the null predicate by its full spellings, so following it never lands in another refusal). The title already says that, so the number is dropped. | record(s) | literal (under `data/`) | now reads | |:--|:--|:--| | #7872 | `filter-comparand-type.test.ts:41` | "the accepted set — one set, defined at the shared door for every driver". The ruling put the comparand-type set at the shared compile face, the measured superset, instead of per-driver policy. | | #5869 | `filter-comparand-type.test.ts:255` | "does not judge a non-array list-operator comparand — that SHAPE is refused 400 by the engine’s shape gate". The record's 500 became an enveloped 400 at the engine's list-shape gate. | | #20347 | `filter-cross-field-comparison-class.test.ts:44` | "the class table — one classification, read by every judge". One comparison classification, lifted from the SQL driver, shared by the lint arms and the driver. | | #8371 | `filter-dotted-head.test.ts:24` | "classifyDottedFilterHead — the FILTER axis dotted verdict, by the type of the head segment". Option 2: a type-directed verdict on the head segment, with the structured head left unjudged. | | #19757 | `filter-empty-operator.test.ts:148` | "the empty list stays refused in the equality slot — the ruled refusal of an array there is untouched". Ruling 乙: an array in the equality slot is refused at the shared face, the empty array included. | | #7597 | `filter-field-reference-lowering.test.ts:56` | "equality triples with a `{ $field }` comparand — lowered to `$eq`, never a bare field spec". | | #20041 | `filter-like-nul-pattern.test.ts:25` | "hasNulInLikePattern — one predicate, so every door refuses a pattern holding U+0000". One spec predicate beside the dangling-escape one, asked at every door. | | #7536 | `filter-like-wire-lowering.test.ts:51` | "the wire lowering of `like` / `ilike` — its own operator, never folded onto `$contains`". The caller binds the wildcards. | | #20116, #19886 | `filter-ne-array-schema-door.test.ts:243` | "§5 — the stored-filter carrier walk refuses $ne: [...] on save too". The save door refuses what the query faces refuse. #19886 is dropped: the title already says the slot refuses an array. | | #20336 (2) | `filter-number-comparand-declared-type.test.ts:59`, `:174` | "the numeric grammar — a JSON number literal naming a finite double" and "numberComparandDoorVerdict — a numeric string narrows to its number, a non-numeric one is refused". | | #20510 | `filter-number-comparand-declared-type.test.ts:308` | "`aggregated` and `boundByDriver` — the refusal says only what is true at its position". The refusal names an aggregated column as such, and names the PostgreSQL server error only where the driver binds. | | #5701, #3948, #6520 | `filter-operator-vocabulary.test.ts:82`, `:89` | One declared expect message: "(measured when $icontains was staged — a dropped predicate WIDENS, which on an RLS read scope is an unauthorized read)" and "Admitting $icontains with an arm on every JS face, in one PR, is the worked example of the clearing direction." | | #4706 | `filter-operator-vocabulary.test.ts:121` | "covers the operators retired when `$icontains` replaced `$regex`". Option B: `$regex` retired, `$icontains` added. | | #8793 | `filter-preset-comparand.test.ts:31` | "FilterConditionSchema — a bare preset name in an ordering comparand is refused at publish". | | #19889 (2) | `filter-save-door-face-parity.test.ts:308`, `:309` | Two declared `it.each` labels: "an implicit list (an equality-slot refusal, unchanged)" and "a $eq list (an equality-slot refusal, unchanged)". Ruling A: the save door refuses an array in the equality slot. | | #18113 | `filter-text-comparand.test.ts:51` | "isRefusedTextComparand — the one predicate every face reads, driven through every FILTER_TEXT_CASES case". Ruling D: the predicate lives with the contract's owner. | | #4706 | `filter-text-conformance.test.ts:162` | A declared expect message: "a case-folding $contains returns both — the answer ruled out when $contains was declared case-sensitive". The record's second question, answered A. | | #15661 | `filter-text-operator-declared-type.test.ts:184` | "textOperatorDoorVerdict — a text operator over a type that never stores a string is refused". C-deny. | **Subject restored (4 ids):** #5222 and #7597 at `filter-comparand-type.test.ts:225` ("leaves a FieldReference alone at every position — cross-field push-down, scalar comparison and the `$eq` lowering own its fate"), #8296 at `filter-dotted-head.test.ts:40` ("a virtual head is refused: the unmaterializable-field verdict finally reaching the dotted spelling") and #19514 at `filter-save-door-face-parity.test.ts:743` ("… and the text-comparand arm stays silent"). **Dropped only (72 ids):** #5146 (2), #5234, #5240, #5298 (3), #5701, #5869, #7536 (2), #7872 (2), #8220, #8793, #8934, #14079, #14426, #15661 (5), #18113 (2), #19514 (3), #19886 (4), #19889 (5), #20116 (7), #20143 (3), #20311 (3), #20336 (5), #20347 (3), #20446, #20502 (8), #20510, #21448 (4). - Each of these titles already states the decision it pins: for example "$ne a value: a row with no value satisfies it" for #5298, "§1 — the enumeration: the save door refuses exactly what the query faces refuse" for #20116, or "matchesLikePattern — `_` is one code point" for #20143. - **The `§1` … `§7` markers stay.** They number each file's own sections, which its comments (`// §1 …`) and header name; only the card number in front of them goes. - **Files that carried a record as a file-wide tag** (`filter-cross-field-comparison-class`, `filter-number-comparand-declared-type`, `filter-text-operator-declared-type`, `filter-like-wire-lowering`, `filter-text-comparand`): the verdict's describe now states the decision (above). The others name the contract part they pin (the judged fields, the refusal message, the fixture, the case table), and their child titles state what each part does. The files' header comments still name the records; comments are not touched here. - **Declared labels and messages:** the five `filter-lowering.test.ts` `NULL_ROWS` names feed `it(row.name)` and the seven `filter-save-door-face-parity.test.ts` labels feed a `%s` title; vitest prints both whole, as the head run's names show. ## Readers - **Test-name filters:** none. No tracked script, workflow or config passes `-t` / `--testNamePattern`. - **Snapshots:** none. No `__snapshots__` directory is tracked under `packages/spec`. - **Projects:** none of the 20 files is listed in `packages/spec/vitest.repo-tests.json`; all 20 run in the `local` project. - **By substring:** every old literal, plus a window around each id (260 needles), was searched across the tracked tree outside its own file. No gate, doc, filter, snapshot or `scripts/check-*.mjs` self-test reads one. The 7 needle hits fall on 5 lines: a code comment in `filter-comparand-type.ts:392`, and sibling titles in `objectql` (`engine-number-comparand-declared-type-door.test.ts:447`, `:459`, `:484`) and `rest` (`data-number-comparand-door.test.ts:269`). None reads a spec test title. - **The files by name:** `scripts/check-driver-conformance.mjs` names `filter-text-conformance.ts`, the source module, not the test file's titles. The other references to these file names are code comments, plus one design-doc line (`docs/design/predicate-compilation-convergence.md:377`) that points at `filter-lowering.test.ts`'s `INTRODUCED` constant, not at a title. ## Text-only proof Stage 10's scratch tool (`textonly10.cjs`, md5 `d5e4801dbb4329ab1984da91e92fc47c`) compares base and head file by file on three legs: 1. **Skeleton:** the full AST, with string pieces masked. It must be identical. 2. **Comments:** every comment, byte-equal. 3. **Strings:** each changed string leaf must sit in a test-call title position or on a declared line, must carry a tracker id before, and must carry no `#` plus digits after. The declared lines are `filter-lowering.test.ts:81`, `:83`, `:85`, `:93` and `:95`, `filter-operator-vocabulary.test.ts:82` and `:89`, `filter-save-door-face-parity.test.ts:308`, `:309`, `:311` to `:314` and `:743`, and `filter-text-conformance.test.ts:162`. - **Result:** 20 of 20 files SAME on all three legs, with the per-file counts predicted in writing before the run. - **Totals:** 96 changed string leaves in 95 literals, 81 titles and 15 declared. The diff's `+` and `-` lines are exactly the 96 planned lines, and every file keeps its line count. - **Controls (10 of 10 as predicted, on scratch copies, each anchor hit once):** identifier rename DIFF; numeric literal DIFF; comment edit COMMENT DIFF; a non-title string given an id VIOLATION; a rewritten title given a new id VIOLATION; a title that was id-free at base edited VIOLATION; one title reverted to base SAME; a declared string given a new id VIOLATION; an undeclared `expect` message changed VIOLATION; a title re-split into a `+` chain DIFF. **Test counts:** the 20 files were run at the base, in a separate base worktree, and at the head, with `--project local --project repo`. Both sides read 671 tests, all passed, with the same count and status sequence per file in 20 of 20. 543 full test names change, and each equals the base name with the planned replacements applied (0 mismatches). No full name repeats on either side. ## `main` since the base Re-fetched just before this PR opened, `origin/main` was three commits past the base (`5b2d189e28`). None of them touches `packages/spec` or any of the 20 files, so `main` was not merged. ## Changeset: `skip-changeset` Measured, not assumed: - `npm pack --dry-run` of `@objectstack/spec` lists 2068 files. 0 of the 20 touched files are in it, and no `*.test.ts` at all. The controls `src/data/filter.zod.ts` and `dist/index.mjs` are in it. - In the built `dist/`, a new phrase and an old literal each read in 0 files. The control `Unrecognized key` reads in 42. So this PR publishes nothing, and no changeset is added. ## Verification (at `2ceeffd7e7`) - `pnpm turbo run build` over all packages: 71 / 71. - `@objectstack/spec`: - `vitest run --project local`: 615 files, 18384 passed, 1 todo. - `typecheck` exit 0, including `check:test-typecheck` (52 files / 246 errors / 135 pinned signatures held). Its program holds all 20 touched files, counted with `tsc --listFilesOnly -p tsconfig.test.json`. - `check:generated`: all 15 generated artifacts up to date. - **Gates:** `dispatch-gates --commands` derived 79 families, the same set as stages 13 to 16, and all 79 exit 0. `--ran` reconciles: 79 derived, 79 run, 0 NOT-MEASURED, 0 UNRUN, every family with its exit code recorded. - The five roster families whose rosters sit under a touched directory were also run, and each exits 0: `check:meta-url-spelling`, `check:spec-changes`, `check:authz-resolver`, `check:error-code-casing` and `check:filter-alias-parity`. - **ESLint, a proven narrowing:** `--no-inline-config` over the 20 files reads 0 errors and 0 warnings. The population comes from ESLint's own config: 20 configured, 0 ignored. No file sets `parserOptions.project` or `projectService`, so no untouched file's verdict can move. - `check-governed-merges --test`: NOT governed, 192 changed lines. ## Acceptance notes - **No needle in this group.** Every id was a title or a declared string; no expected value of an assertion over a source docblock was found. The three known needles are untouched. - **Same-id test titles in this card's later stages** go with those stages: 6 lines in `packages/spec/src`. Two are in the next `data/` group (`data/filter.test.ts:576` and `:679`), and four are in `ui/` (`component-object-grid-default-filters.pin.test.ts:56`, `component.test.ts:3518`, `dataset-filter-nested-relation-list.test.ts:220`, `view-filter-rule-value-shape.test.ts:249`). - **Same-id test titles in other packages** are their lanes' test-string shares. A search of `describe` / `it` / `test` lines outside `packages/spec` finds 187 lines in 16 packages and one example app citing ids this PR handled: `driver-sql` 32 (14 files), `service-analytics` 32 (18), `formula` 24 (9), `lint` 21 (8), `objectql` 21 (11), `driver-memory` 13 (10), `driver-turso` 11 (10), `plugin-security` 9 (5), `driver-mongodb` 8 (5), `driver-sqlite-wasm` 6 (5), `rest` 3 (2), `cli` 2, `metadata-protocol` 2, and one each in `core`, `runtime` and `examples/app-todo`. - **Code comments still carry ids** in these files and their sources, for example the `// [#21448]` line above `filter-save-door-face-parity.test.ts:311` and the header of `filter-operator-vocabulary.test.ts`. Comments are not this card's share, and none is touched here. --- _Generated by [Claude Code](https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 2df3d13 commit 969ffba

20 files changed

Lines changed: 96 additions & 96 deletions

‎packages/spec/src/data/filter-comparand-type.test.ts‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class Money {
3838
constructor(readonly v: number) {}
3939
}
4040

41-
describe('the accepted set (#7872 ruling)', () => {
41+
describe('the accepted set — one set, defined at the shared door for every driver', () => {
4242
it('is exactly the measured superset — string | number | bigint | boolean | null | Date', () => {
4343
expect([...ACCEPTED_FILTER_COMPARAND_TYPES]).toEqual([
4444
'string', 'number', 'bigint', 'boolean', 'null', 'Date',
@@ -118,7 +118,7 @@ describe('refusals — the measured divergence rows die at the door', () => {
118118
expect(err?.message).toContain('where.qty');
119119
});
120120

121-
it.each(refused)('refuses %s as an $in member — each member is a comparand in its own right (#5234)', (_name, value) => {
121+
it.each(refused)('refuses %s as an $in member — each member is a comparand in its own right', (_name, value) => {
122122
const err = refusalOf(() => normalizeFilterComparandTypes({ qty: { $in: [100, value] } }));
123123
expect(err?.code).toBe('INVALID_FILTER');
124124
expect(err?.message).toContain('where.qty.$in[1]');
@@ -131,7 +131,7 @@ describe('refusals — the measured divergence rows die at the door', () => {
131131
expect(err?.message).toMatch(/omit/);
132132
});
133133

134-
it('the undefined prescription is position-safe: it names the null predicate by its ruled spellings, at every position it is emitted at (#14426)', () => {
134+
it('the undefined prescription is position-safe: it names the null predicate by its ruled spellings, at every position it is emitted at', () => {
135135
// "Write null for the null predicate" was position-agnostic advice: followed
136136
// at `$gt: undefined` it produced `$gt: null`, refused one door over since
137137
// the 2026-09-01 ruling; at an `$in` member it produced `$in: [null]`,
@@ -183,7 +183,7 @@ describe('refusals — the measured divergence rows die at the door', () => {
183183
});
184184
});
185185

186-
describe('bigint — accepted, and NARROWED copy-on-write (#7872; the memory crash cell dies here)', () => {
186+
describe('bigint — accepted, and NARROWED copy-on-write (the memory crash cell dies here)', () => {
187187
it('narrows an exact-range bigint to its number, without touching the caller’s object', () => {
188188
const original = { qty: { $eq: BigInt(100) } };
189189
const out = normalizeFilterComparandTypes(original);
@@ -222,7 +222,7 @@ describe('bigint — accepted, and NARROWED copy-on-write (#7872; the memory cra
222222
});
223223

224224
describe('boundaries the door deliberately does not cross', () => {
225-
it('leaves a FieldReference alone at every position — #5222/scalar comparison/#7597 own its fate', () => {
225+
it('leaves a FieldReference alone at every position — cross-field push-down, scalar comparison and the `$eq` lowering own its fate', () => {
226226
for (const filter of [
227227
{ amount: { $gt: { $field: 'budget' } } },
228228
{ amount: { $eq: { $field: 'budget' } } },
@@ -233,7 +233,7 @@ describe('boundaries the door deliberately does not cross', () => {
233233
}
234234
});
235235

236-
it('does not descend into a no-$-key plain object — nested-relation / deep-equality structure (#5869 boundary)', () => {
236+
it('does not descend into a no-$-key plain object — nested-relation / deep-equality structure', () => {
237237
const filter = { author: { name: 'x' } };
238238
expect(normalizeFilterComparandTypes(filter)).toBe(filter);
239239
});
@@ -252,18 +252,18 @@ describe('boundaries the door deliberately does not cross', () => {
252252
expect(normalizeFilterComparandTypes(retired)).toBe(retired);
253253
});
254254

255-
it('does not judge a non-array list-operator comparand — that SHAPE belongs to the engine’s #5869 gate', () => {
255+
it('does not judge a non-array list-operator comparand — that SHAPE is refused 400 by the engine’s shape gate', () => {
256256
const filter = { stage: { $in: 'won' } };
257257
expect(normalizeFilterComparandTypes(filter)).toBe(filter);
258258
});
259259

260-
it('keeps the zero-operator constraint for the driver refusal that names it (#5240)', () => {
260+
it('keeps the zero-operator constraint for the driver refusal that names it', () => {
261261
const filter = { qty: {} };
262262
expect(normalizeFilterComparandTypes(filter)).toBe(filter);
263263
});
264264
});
265265

266-
describe('parseFilterAST is the compile face that runs the door (#7872)', () => {
266+
describe('parseFilterAST is the compile face that runs the door', () => {
267267
it('judges the OBJECT passthrough — the form that used to leave unexamined', () => {
268268
const err = refusalOf(() => parseFilterAST({ qty: { $eq: Symbol('x') } }));
269269
expect(err?.code).toBe('INVALID_FILTER');

‎packages/spec/src/data/filter-cross-field-comparison-class.test.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const union = (...sets: ReadonlySet<string>[]) => new Set(sets.flatMap((s) => [.
4141
const cls = (c: string): CrossFieldColumnVerdict => ({ kind: 'class', class: c } as CrossFieldColumnVerdict);
4242
const none = (r: string): CrossFieldColumnVerdict => ({ kind: 'no-class', reason: r } as CrossFieldColumnVerdict);
4343

44-
describe('[#20347] the class table', () => {
44+
describe('the class table — one classification, read by every judge', () => {
4545
it('classifies every FieldType member exactly once — rows pairwise disjoint, union exactly FieldType', () => {
4646
const seen = new Map<string, string>();
4747
for (const row of CROSS_FIELD_COMPARISON_TYPE_CLASSES) {
@@ -107,7 +107,7 @@ describe('[#20347] the class table', () => {
107107
});
108108
});
109109

110-
describe('[#20347] crossFieldColumnVerdict — one declared column', () => {
110+
describe('crossFieldColumnVerdict — one declared column', () => {
111111
it('answers each FieldType member its row\'s verdict when declared single-valued', () => {
112112
for (const row of CROSS_FIELD_COMPARISON_TYPE_CLASSES) {
113113
for (const type of row.types) {
@@ -140,7 +140,7 @@ describe('[#20347] crossFieldColumnVerdict — one declared column', () => {
140140
});
141141
});
142142

143-
describe('[#20347] crossFieldComparisonVerdict — the measured cells', () => {
143+
describe('crossFieldComparisonVerdict — the measured cells', () => {
144144
it('text vs number is cross-class, in both orders', () => {
145145
expect(crossFieldComparisonVerdict({ type: 'text' }, { type: 'number' }))
146146
.toEqual({ verdict: 'cross-class', left: 'text', right: 'numeric' });
@@ -183,7 +183,7 @@ describe('[#20347] crossFieldComparisonVerdict — the measured cells', () => {
183183
});
184184
});
185185

186-
describe('[#20347] crossFieldComparisonVerdict — every pair of declared columns', () => {
186+
describe('crossFieldComparisonVerdict — every pair of declared columns', () => {
187187
// Every FieldType member single-valued, plus every multi-capable member
188188
// flagged `multiple: true`.
189189
const columns = [

‎packages/spec/src/data/filter-dotted-head.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { SEARCH_VIRTUAL_TYPES } from './search-fields';
2121
// DELIBERATELY unjudged (structured/JSON — live on two of three backends, the
2222
// carve-out the maintainer ruling names with a ⛔).
2323
// ---------------------------------------------------------------------------
24-
describe('[#8371] classifyDottedFilterHead — the FILTER axis dotted verdict', () => {
24+
describe('classifyDottedFilterHead — the FILTER axis dotted verdict, by the type of the head segment', () => {
2525
it('a relation head is refused: the stored value is a scalar id, not an embedded document', () => {
2626
expect(classifyDottedFilterHead({ type: 'lookup' })).toBe('relation');
2727
expect(classifyDottedFilterHead({ type: 'master_detail' })).toBe('relation');
@@ -37,7 +37,7 @@ describe('[#8371] classifyDottedFilterHead — the FILTER axis dotted verdict',
3737
}
3838
});
3939

40-
it('a virtual head is refused: the #8296 verdict finally reaching the dotted spelling', () => {
40+
it('a virtual head is refused: the unmaterializable-field verdict finally reaching the dotted spelling', () => {
4141
for (const type of SEARCH_VIRTUAL_TYPES) {
4242
expect(classifyDottedFilterHead({ type }), `'${type}' must classify as virtual`).toBe('virtual');
4343
}

‎packages/spec/src/data/filter-empty-operator.test.ts‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ function descriptionOf(shape: Record<string, unknown>, key: string): string | un
8282
// §1 The description IS the ruled table
8383
// ---------------------------------------------------------------------------
8484

85-
describe('#20311 §1 — the $empty description is the ruled per-type table', () => {
85+
describe('§1 — the $empty description is the ruled per-type table', () => {
8686
/** The ruled table, verbatim as the operator describes it. */
8787
const RULED_TABLE =
8888
'Is-empty check by the field\'s DECLARED type. `true` matches rows whose field is empty, '
@@ -118,7 +118,7 @@ describe('#20311 §1 — the $empty description is the ruled per-type table', ()
118118
// §2 The schema door: $empty parses; the empty list is still refused
119119
// ---------------------------------------------------------------------------
120120

121-
describe('#20311 §2 — { tags: { $empty: true } } parses; { tags: [] } is still refused', () => {
121+
describe('§2 — { tags: { $empty: true } } parses; { tags: [] } is still refused', () => {
122122
it('the enforced operator slot keeps a boolean $empty rather than stripping it', () => {
123123
// An undeclared key on this non-strict object is STRIPPED on parse; a
124124
// declared one survives. `toEqual` holding both values is the difference.
@@ -145,7 +145,7 @@ describe('#20311 §2 — { tags: { $empty: true } } parses; { tags: [] } is stil
145145
expect(NormalizedFilterSchema.safeParse({ $and: [{ tags: { $empty: 'yes' } }] }).success).toBe(false);
146146
});
147147

148-
it('the empty list stays refused in the equality slot — ruling 乙 on #19757 is untouched', () => {
148+
it('the empty list stays refused in the equality slot — the ruled refusal of an array there is untouched', () => {
149149
for (const [where, path] of [
150150
[{ tags: [] }, 'tags'],
151151
[{ tags: { $eq: [] } }, 'tags.$eq'],
@@ -178,7 +178,7 @@ describe('#20311 §2 — { tags: { $empty: true } } parses; { tags: [] } is stil
178178
// §3 The expansion: three arms for the three field kinds, and the value test
179179
// ---------------------------------------------------------------------------
180180

181-
describe('#20311 §3 — expandEmptyOperator answers the ruled arm per field definition', () => {
181+
describe('§3 — expandEmptyOperator answers the ruled arm per field definition', () => {
182182
it('is published on the data entry — the one function every compile surface imports', () => {
183183
expect(dataBarrel.expandEmptyOperator).toBe(expandEmptyOperator);
184184
expect(dataBarrel.isEmptyFilterValue).toBe(isEmptyFilterValue);
@@ -249,7 +249,7 @@ describe('#20311 §3 — expandEmptyOperator answers the ruled arm per field def
249249
// §4 The flip: in FILTER_OPERATORS, and the view operators lower to it
250250
// ---------------------------------------------------------------------------
251251

252-
describe('#20446 §4 — $empty is IN FILTER_OPERATORS and is_empty / is_not_empty lower to it', () => {
252+
describe('§4 — $empty is IN FILTER_OPERATORS and is_empty / is_not_empty lower to it', () => {
253253
it('is in FILTER_OPERATORS, beside the other value-presence flags', () => {
254254
// Inverted from #20311's staging pin (the maintainer's amendment, record
255255
// 5868169573): every compile face answers `$empty` now (#20444, #20445),

‎packages/spec/src/data/filter-equality-array-schema-door.test.ts‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ const REFUSED: ReadonlyArray<readonly [label: string, where: unknown, issuePath:
8787
// §1 FilterConditionSchema refuses both spellings, at the slot's own path
8888
// ---------------------------------------------------------------------------
8989

90-
describe('#19889 §1 — FilterConditionSchema refuses an array in the equality slot', () => {
90+
describe('§1 — FilterConditionSchema refuses an array in the equality slot', () => {
9191
it.each(REFUSED)('refuses %s', (_label, where, issuePath) => {
9292
const issue = issueAt(FilterConditionSchema.safeParse(where), issuePath);
9393
expect(issue.message).toMatch(/requires a single comparable value, but received an array/);
@@ -104,7 +104,7 @@ describe('#19889 §1 — FilterConditionSchema refuses an array in the equality
104104
// §2 FieldOperatorsSchema.$eq refuses — the enforced copy and the documented one
105105
// ---------------------------------------------------------------------------
106106

107-
describe('#19889 §2 — the $eq operator slot refuses an array', () => {
107+
describe('§2 — the $eq operator slot refuses an array', () => {
108108
it.each([
109109
['FieldOperatorsSchema (the enforced copy)', FieldOperatorsSchema],
110110
['EqualityOperatorSchema (the documentation copy)', EqualityOperatorSchema],
@@ -125,7 +125,7 @@ describe('#19889 §2 — the $eq operator slot refuses an array', () => {
125125
// §3 The parse-door message IS the compile-face message
126126
// ---------------------------------------------------------------------------
127127

128-
describe('#19889 §3 — one text, two doors', () => {
128+
describe('§3 — one text, two doors', () => {
129129
it.each(REFUSED)('%s — the schema door prints the face\'s sentence', (_label, where, issuePath, facePath) => {
130130
const face = faceRefusal(where);
131131
// The face is the ADR-0112 class-1 refusal it has been since #19757.
@@ -174,7 +174,7 @@ describe('#19889 §3 — one text, two doors', () => {
174174
// §4 CONTROLS — both doors accept, so the arm is exactly the face's
175175
// ---------------------------------------------------------------------------
176176

177-
describe('#19889 §4 — what stays accepted, at BOTH doors', () => {
177+
describe('§4 — what stays accepted, at BOTH doors', () => {
178178
const day = new Date('2026-07-01T00:00:00.000Z');
179179
it.each([
180180
['a string', { stage: 'won' }],
@@ -244,7 +244,7 @@ describe('#19889 §4 — what stays accepted, at BOTH doors', () => {
244244
// §5 The stored carriers refuse on save, naming the path
245245
// ---------------------------------------------------------------------------
246246

247-
describe('#19889 §5 — a stored filter carrier refuses the shape on save', () => {
247+
describe('§5 — a stored filter carrier refuses the shape on save', () => {
248248
const dataset = (extra: Record<string, unknown>) => ({
249249
name: 'deals_ds',
250250
label: 'Deals',

‎packages/spec/src/data/filter-field-reference-lowering.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ const EQUALITY_SPELLINGS = ['=', '==', 'equals', 'eq'] as const;
5353

5454
const REF = { $field: 'budget' } as const;
5555

56-
describe('[#7597] equality triples with a `{ $field }` comparand', () => {
56+
describe('equality triples with a `{ $field }` comparand — lowered to `$eq`, never a bare field spec', () => {
5757
// ── The fix ───────────────────────────────────────────────────────────────
5858

5959
for (const op of EQUALITY_SPELLINGS) {

‎packages/spec/src/data/filter-icontains-parse-door.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ function issueAt(
8080
// §1 the `$` dialect door — FilterConditionSchema
8181
// ---------------------------------------------------------------------------
8282

83-
describe('#19514 §1 — the $ dialect refuses what the table declares refused', () => {
83+
describe('§1 — the $ dialect refuses what the table declares refused', () => {
8484
it.each([
8585
['the EMPTY comparand', '', 'EMPTY STRING'],
8686
['a NUMBER comparand', 42, 'not a string'],
@@ -156,7 +156,7 @@ describe('#19514 §1 — the $ dialect refuses what the table declares refused',
156156
// §2 the infix/view dialect door — ViewFilterRuleSchema
157157
// ---------------------------------------------------------------------------
158158

159-
describe('#19514 §2 — the view vocabulary refuses the same two comparands', () => {
159+
describe('§2 — the view vocabulary refuses the same two comparands', () => {
160160
const rule = (value?: unknown) =>
161161
ViewFilterRuleSchema.safeParse(
162162
value === undefined
@@ -241,7 +241,7 @@ describe('#19514 §2 — the view vocabulary refuses the same two comparands', (
241241
// §3 both doors are DRIVEN BY the table, not by a copy of it
242242
// ---------------------------------------------------------------------------
243243

244-
describe('#19514 §3 — a row added to FILTER_TEXT_CASES reaches both doors', () => {
244+
describe('§3 — a row added to FILTER_TEXT_CASES reaches both doors', () => {
245245
it('the table still carries rows for this operator, so this suite is not vacuous', () => {
246246
expect(TABLE_ROWS.length).toBeGreaterThan(0);
247247
});

‎packages/spec/src/data/filter-like-code-point.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const FAMILY: ReadonlyArray<readonly ['$like' | '$ilike', string, readonly strin
5757
const answer = (match: (v: string) => boolean): string[] =>
5858
Object.entries(ROWS).filter(([, v]) => match(v)).map(([label]) => label).sort();
5959

60-
describe('[#20143] matchesLikePattern — `_` is one code point', () => {
60+
describe('matchesLikePattern — `_` is one code point', () => {
6161
for (const [op, pattern, codePoint] of FAMILY) {
6262
it(`${op} ${JSON.stringify(pattern)} answers the code-point rows`, () => {
6363
expect(answer((v) => matchesLikePattern(v, pattern, op === '$ilike'))).toEqual([...codePoint]);
@@ -90,7 +90,7 @@ describe('[#20143] matchesLikePattern — `_` is one code point', () => {
9090
});
9191
});
9292

93-
describe('[#20143] likePatternToRegExp — the one compilation', () => {
93+
describe('likePatternToRegExp — the one compilation', () => {
9494
it('carries the `u` flag and nothing else (no `i`: the $ilike fold is in the source)', () => {
9595
expect(likePatternToRegExp('a_b').flags).toBe('u');
9696
expect(likePatternToRegExp('a_b', true).flags).toBe('u');
@@ -111,7 +111,7 @@ describe('[#20143] likePatternToRegExp — the one compilation', () => {
111111
});
112112
});
113113

114-
describe('[#20143] every ASCII punctuation character is `u`-legal, bare and escaped', () => {
114+
describe('every ASCII punctuation character is `u`-legal, bare and escaped', () => {
115115
/** Printable ASCII that is neither a letter nor a digit, space included: 33 characters. */
116116
const PUNCTUATION = Array.from({ length: 0x7f - 0x20 }, (_, i) => String.fromCharCode(0x20 + i))
117117
.filter((c) => !/[A-Za-z0-9]/.test(c));

‎packages/spec/src/data/filter-like-nul-pattern.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222

2323
const NUL = String.fromCharCode(0x00);
2424

25-
describe('[#20041] hasNulInLikePattern', () => {
25+
describe('hasNulInLikePattern — one predicate, so every door refuses a pattern holding U+0000', () => {
2626
it('finds U+0000 at the start, in the middle, at the end, and alone', () => {
2727
expect(hasNulInLikePattern(NUL + '%')).toBe(true);
2828
expect(hasNulInLikePattern('a' + NUL + 'b')).toBe(true);

‎packages/spec/src/data/filter-like-wire-lowering.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import {
4848
matchesLikePattern,
4949
} from './filter.zod';
5050

51-
describe('[#7536] the wire lowering of `like` / `ilike`', () => {
51+
describe('the wire lowering of `like` / `ilike` — its own operator, never folded onto `$contains`', () => {
5252
// ── The card's repro table, as pins ───────────────────────────────────────
5353

5454
it('keeps a wildcard pattern INTACT — the `%` is the caller\'s, not a literal', () => {
@@ -153,7 +153,7 @@ describe('[#7536] the wire lowering of `like` / `ilike`', () => {
153153
});
154154
});
155155

156-
describe('[#7536] the `$like` pattern language', () => {
156+
describe('the `$like` pattern language', () => {
157157
// One definition, shared by every face — so these cases are the contract the
158158
// SQL emitters and the JS evaluator are BOTH held to.
159159

@@ -220,7 +220,7 @@ describe('[#7536] the `$like` pattern language', () => {
220220
});
221221
});
222222

223-
describe('[#7536] the LIKE → GLOB translation the SQLite dialects need', () => {
223+
describe('the LIKE → GLOB translation the SQLite dialects need', () => {
224224
// SQLite's `LIKE` folds ASCII case and cannot be told not to per statement,
225225
// so a case-exact pattern match has to use `GLOB` — which speaks a DIFFERENT
226226
// pattern language. These cases are what stops the two from being confused.

0 commit comments

Comments
 (0)