Skip to content

Commit 124533b

Browse files
test(spec): the fourth data/ file group's test titles state each cited decision in words instead of a tracker number (stage 18) (#21870)
Part of #20749 Clause-②: no Stage 18 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 fourth name-ordered file group directly under `packages/spec/src/data/`: the 17 test files that carry an id from `filter.test.ts` to `object.test.ts`. They carried 103 messages and 114 tracker ids, citing 81 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 (`c9be1f179d`) 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 17 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. The base is `c9be1f179d`, one commit past the claim's `969ffba25e`. That commit (#21857) touches only `plugin-security`, so the test census is the same. Both instruments read **1045 messages / 1108 ids in 225 files**, the seat's reading and stage 17's head reading. | directory | files | messages / ids | titles | other | |:--|--:|--:|--:|--:| | `ui/` | 84 | 396 / 419 | 378 / 401 | 18 / 18 | | `api/` | 40 | 189 / 201 | 181 / 193 | 8 / 8 | | `data/` (this PR: the fourth group) | 35 | 185 / 200 | 184 / 199 | 1 / 1 | | `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** | **225** | **1045 / 1108** | **988 / 1050** | **57 / 58** | The group reads **103 messages / 114 ids in 17 files**, the seat's figures, file for file. Every one of them is a test title: | file (under `data/`) | messages / ids | titles | other | |:--|--:|--:|--:| | `filter.test.ts` | 18 / 19 | 18 / 19 | 0 | | `form-delete-behavior-options.test.ts` | 1 / 1 | 1 / 1 | 0 | | `form-return-type-options.test.ts` | 1 / 1 | 1 / 1 | 0 | | `hook-body.test.ts` | 4 / 4 | 4 / 4 | 0 | | `hook.test.ts` | 10 / 10 | 10 / 10 | 0 | | `import-coercion.test.ts` | 2 / 2 | 2 / 2 | 0 | | `import-mapping-target.test.ts` | 2 / 2 | 2 / 2 | 0 | | `injected-system-column-provenance.test.ts` | 2 / 2 | 2 / 2 | 0 | | `injected-system-columns.test.ts` | 1 / 1 | 1 / 1 | 0 | | `inline-grid-column-currency-scale-refused.test.ts` | 5 / 5 | 5 / 5 | 0 | | `inline-related-columns.test.ts` | 3 / 3 | 3 / 3 | 0 | | `managed-api-affordance.test.ts` | 2 / 2 | 2 / 2 | 0 | | `masked-field-types.test.ts` | 1 / 1 | 1 / 1 | 0 | | `numeric-column-representation.test.ts` | 1 / 1 | 1 / 1 | 0 | | `object-image-field.test.ts` | 1 / 1 | 1 / 1 | 0 | | `object-strictness-batch20.test.ts` | 13 / 17 | 13 / 17 | 0 | | `object.test.ts` | 36 / 42 | 36 / 42 | 0 | | **17 files** | **103 / 114** | **103 / 114** | **0** | Five more test files sit in the same name range and carry no id: `hook-api`, `hook-body-stored-metadata-target`, `inline-grid-columns`, `mapping-connector-source` and `mapping`. They are not touched. - **Controls.** Lit, a title and an "other" string: `data/query.test.ts`, outside the group, reads 11 / 11 at the head as at the base, its "other" string at `:202` included. Dark: `data/import-mapping-target.test.ts` reads 0 / 0 at the head while 2 of its comment lines still carry a number. Planted in scratch copies of head files: an id put into a `hook-body.test.ts` title reads 1 / 1, and an id put into a `masked-field-types.test.ts` comment reads 0. - **A wider pattern** (any `#` plus digits) reads the same totals as the gate pattern in 16 of the 17 files at the base. `object.test.ts` reads one more, at `:857`, which is the colour value `'#00FF00'` and not a tracker id. At the head the wider pattern reads 0 in 16 files and that same colour in `object.test.ts`. - **At the head:** 942 messages / 994 ids in 208 files. The 17 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 17's re-cut named this group at 114, with `object.test.ts` alone carrying 42, and this census reads 114, so the rule needed no re-cut. **Named for the next stages** (re-cut from the head census, 942 / 994; `data/` 82 / 86 left, in 18 files): - `data/`, one more stage: `query-transport.test.ts` to `validation.test.ts` (11 files, 34 / 34) with `data/driver/` (7 files, 48 / 52): 82 messages / 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 34 literals (41 ids) now state a decision in words. 69 literals (73 ids) drop a number the title already explains. Every cited record was read with its comments through REST. 73 answer 200; `framework#2536` is counted there, because the repository was renamed `framework` → `objectstack` (`apps/docs/lib/layout.shared.tsx` records the rename). The old name answers 403 from this session, and the same number under the current name is the compactLayout retirement that the title names. Eight answer 404, and each decision was read from what landed, by REST GET of the landing commit and its CHANGELOG entry: #6571 (`2f3e79351e`), #10165 (`8012960508`), #10347 (`530c1df653`), #10527 (`5649efbf93`), #11195 (`b372318836`), #11408 (`f11fc61c51`), #13644 (`34ce8e7dbe`) and #18012 (`176b03582e`). | record(s) | literal (under `data/`) | now reads | |:--|:--|:--| | #5685 | `filter.test.ts:77` | "string comparands — the ordering slots take a string, which is what the date macros emit". `string` joined the four ordering slots because the platform's own date-macro resolver produces only strings. | | #14080 | `filter.test.ts:166` | "null comparand — refused in the four ordering slots, like a null list member". Ruled A: refused at the same entrance, in the same shape, as the null `$in` member. | | #6571 (404) | `filter.test.ts:364` | "string endpoints — `$between` takes the ISO and clock strings the platform produces". The sibling half of #5685, from `2f3e79351e`. | | #7711 (2) | `filter.test.ts:549` | "the whole-filter face refuses these field conditions too — green while the group branch was a non-strict catch-all". The group branch became `.strict()`, so a refused member has nowhere to land. | | #5222 | `filter.test.ts:576` | "leaves the four ORDERING slots taking a reference — a column-to-column comparison, and the prescribed alternative". `$field` compiles to a same-table column comparison on SQL push-down. | | #5322 | `filter.test.ts:1541` | "leaves the empty-combinator identities accepted — each reduces to its boolean unit". Ruled: `{$and:[]}` is every row, `{$or:[]}` none, `{$not:{}}` none. | | #14104 | `filter.test.ts:1851` | "FieldReferenceSchema.addDays — a whole-day offset on a reference, an integer or another column". Ruled A: an offset on the field reference. | | #16923 | `filter.test.ts:1998` | "filter.zod.ts docblock @examples — a `$field` comparand names a column of the same row". The relation-path example was the wrong half; the same-table prose was right. | | #14010 | `hook.test.ts:352` | "runAs — a hook may run as `system` or `user`, and inherits by default". Ruled: `runAs: 'system' \| 'user' \| 'inherit'`, default `'inherit'`. | | #13644 (404) | `hook.test.ts:529` | "Referential-Cleanup Marker — declared, and true only on a reference-cleanup write by the engine". Adopted by maintainer ruling, from `34ce8e7dbe`. | | #4269 | `hook.test.ts:924` | "defineHook — the authoring factory, so a hook is validated where it is written". The `defineDatasource` pattern: the convention-scan path got a parse at authoring time. | | #3493 | `hook.test.ts:1148` | "PRESERVES `preserveAudit` through a parse (the opt-in a historical import uses to keep its audit stamps)". | | #5945 | `hook.test.ts:1286` | "HookContext.api typing — the minimum scoped context the docs teach: `object()` and `transaction()`". Ruled option C. | | #4173 (2) | `import-coercion.test.ts:23`, `:48` | "import boolean tokens — one table for the server coercion and the Import Wizard preview" and "import reference types — exported from spec, not copied at each consumer". | | #8116 | `injected-system-column-provenance.test.ts:53` | "provenance derivation at its spec home — where the author-time linter can reach it". Ruled option 1: the derivation moved into the contract package, which the linter may import. | | #5378 | `injected-system-columns.test.ts:17` | "resolveInjectedSystemColumns — the injected columns, so author-time validation resolves them too". | | #20045 | `inline-grid-column-currency-scale-refused.test.ts:172` | "SHAPE PARITY with the currency FIELD refusal (its ruled text carried, not reworded)". "ruling B" is the ruling that retired `scale` on a currency field; the letter went with the id. | | #7521 | `managed-api-affordance.test.ts:57` | "is the exact shape the boot only warned about, now named at authoring time (sys_environment / sys_package)". Ruled: an authoring-time check, with boot left at warn and strip. | | #16318 | `numeric-column-representation.test.ts:24` | "the numeric physical-representation table — one table the driver and the migration generators both read". Ruled C: one table in `packages/spec`, both producers read it. | | #4001 (2) | `object-strictness-batch20.test.ts:93`, `:229` | "批 20, unknown keys refused — …", as stage 15 wrote "batch D, unknown keys refused". `:132` already says it and keeps only "批 20". | | #1535, #4519, #4522 | `object-strictness-batch20.test.ts:124` | "the root itself was already closed, on parse as well as create() — this batch is the level BELOW it". | | #5014 | `object-strictness-batch20.test.ts:187` | "⚠️ `systemFields` is the batch's ONE union flattened to a bare `Invalid input` — …". | | #5677, #6365 | `object-strictness-batch20.test.ts:380` | "… — since the unit anchor is injected, the property governs `owning_business_unit_id` too". #6365 is dropped: the title already states its correction. | | #11195 (404) | `object-strictness-batch20.test.ts:422` | "the two `userActions` vocabularies stay disjoint, the three adopted view keys included — …". `b372318836` adopted `group` / `hideFields` / `rowColor` onto the view block. | | #4001, objectui#4772 | `object-strictness-batch20.test.ts:528` | "批 20 — `IndexSchema` is closed (the held 14th site, once the console index editor converged on it)". | | #10527 (404) | `object.test.ts:188` | "retention + ttl + archive triple — refused unless the ttl restates the age bound". From `5649efbf93`. | | #10347 (404) | `object.test.ts:192` | "still accepts the ttl + archive pair, whose ttl cutoff picks the rows to archive (no retention)". From `530c1df653`. | | #2834 | `object.test.ts:264` | "accepts retention.onlyWhen with scalar and $in predicates (mixed tables, where only terminal rows age out)". | | #10165 (404) | `object.test.ts:289` | "accepts ttl.onlyWhen with the canonical null predicate — so rows whose value is absent are spared". From `8012960508`. | | #3175 | `object.test.ts:1101` | "ownership record-model field — declared, so the opt-out the registry reads can be authored". | | #11408 (404), #10144 | `object.test.ts:1713` | "ObjectSchema editMode (declared by maintainer ruling: the renderer reads it, so the spec declares it)". From `f11fc61c51`, in #10144's declare-or-rule-out family. | | #14935, #14637 | `object.test.ts:2135` | "isPublicSharingEnabled — the one canonical standing share-link policy predicate". The runtime mirror was retired. | **Dropped where already stated** (69 literals, 73 ids). A number goes only where the title already says its decision, for example "$field members are refused (#7596)", the four `crypto.hash` titles in `hook-body.test.ts` (#4391), the `#20045 —` prefix on the four other describes of the currency-scale file, and twenty-nine `object.test.ts` titles such as "managedBy: retiring the overloaded `system` bucket (#3355)". `(ADR-0049)`, `(ADR-0066)`, `(ADR-0100)` and `(ADR-0087 …)` stay: they cite decision records by number, not tracker ids. **Three judgements, each declared:** - `filter.test.ts:298` keeps "ruled 2026-08-31" and drops only `(#13357)`. The sibling title at `:656` names "the 2026-08-31 ruling", so the date stays as its anchor. - The `批 20` label stays on the four batch-20 describes, because the file's own name and header carry it. Only `#4001` and `objectui#4772` went. - `object-strictness-batch20.test.ts:563` drops "(#5114 class)": the title already says what is pinned, that the tombstones keep their prescription through the strict close. ## 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 17 files is listed in `packages/spec/vitest.repo-tests.json`; all 17 run in the `local` project. - **By substring:** every old literal, plus a window around each id (311 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 8 needle hits fall on 7 lines: - a code comment in `object.zod.ts:2532`; - two release-owned CHANGELOG lines; - a sibling title in this card's `system/` stage (`system/job.test.ts:471`); - sibling titles in `cli` (`extract-hook-body.test.ts:179`), `driver-sql` (`sql-driver-16318-numeric-representation.test.ts:64`) and `objectql` (`engine.test.ts:831`). None reads a spec test title. - **The files by name:** 96 references to these file names outside CHANGELOGs. The gate ledgers among them (`test-typecheck-debt.json`, `engine-double-contract.pinned.json`, `objectql-double-limit.baseline.json`) key on the file and on error signatures, not on a title, and each gate exits 0 at the head. `scripts/check-org-identifier.mjs` counts `session: { … tenantId … }` literals in `hook.test.ts`, which this PR does not touch. ADR-0129 quotes "name-as-identity", a title this PR does not change. ## 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. This stage declares no line: every changed leaf is a title. - **Result:** 17 of 17 files SAME on all three legs, with the per-file counts predicted in writing before the run. - **Totals:** 103 changed string leaves in 103 literals, all titles. The diff's `+` and `-` lines are exactly the 103 planned lines, and every file keeps its line count. - **Controls (10 of 10 as predicted, on scratch copies, each anchor hit once in the reported run):** 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; an `it.each` row name given an id VIOLATION; an undeclared `expect` message changed VIOLATION; a title re-split into a `+` chain DIFF. The non-title control's first anchor matched nothing (0 hits, so nothing ran). Its anchor was corrected and all ten were run again. **Test counts:** the 17 files were run at the base, in a separate base worktree, and at the head, with `--project local --project repo`. Both sides read 670 tests, all passed, with the same count and status sequence per file in 17 of 17. 378 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 two commits past the base (`9f9510f25e`: #21858, #21862). Neither touches any of the 17 files; the one `packages/spec` path among them is `api/error-code-ledger.zod.ts`. So `main` was not merged. `git merge-tree` onto `9f9510f25e` is clean. No open PR touches the 17 files. ## Changeset: `skip-changeset` Measured, not assumed: - `npm pack --dry-run` of `@objectstack/spec` lists 2068 files. 0 of the 17 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 `21f39afe57`) - `pnpm turbo run build` over all packages: 71 / 71. - `@objectstack/spec`: - `vitest run --project local`: 615 files, 18387 passed, 1 todo. - `typecheck` exit 0, including `check:test-typecheck` (52 files / 246 errors / 135 pinned signatures held). Its program holds all 17 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 17, 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`. - The derivation printed `STALE TREE`: `scripts/engine-double-contract.pinned.json` moved on `main` after the base. This diff adds and changes no engine double, `check:engine-double-contract` exits 0 on this tree, and the queue re-runs it on the merged generation. - **ESLint, a proven narrowing:** `--no-inline-config` over the 17 files reads 0 errors and 0 warnings. The population comes from ESLint's own config: 17 configured, 0 ignored. No file sets `parserOptions.project` or `projectService`, so no untouched file's verdict can move. - `check-governed-merges --test`: NOT governed, 206 changed lines. ## Acceptance notes - **No needle in this group.** Every id was a test title; 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: 38 lines in `packages/spec/src`, 9 in `system/` (for example `system/job.test.ts:471`, the `job.timeout` twin of `hook.test.ts:1382`) and 29 in `ui/` (for example the `#4001 批 15` / `批 16` / `批 18` / `批 19` describes and `ui/view-filter-rule-wire-id.test.ts`'s four `#5114` describes). - **Same-id test titles in other packages** are their lanes' test-string shares. A search of `describe` / `it` / `test` lines outside `packages/spec/src` finds 140 lines citing ids this PR handled: `objectql` 23 (13 files), `service-analytics` 18 (8), `lint` 17 (9), `driver-sql` 11 (7), `cli` 10 (4), `runtime` 10 (7), `platform-objects` 5, `plugin-audit` 5, `plugin-security` 5, and fewer in 16 more places, among them two `packages/spec/scripts/*.test.ts` titles (outside `src/`) and one title each in `examples/app-crm` and `examples/app-showcase`. - **Code comments still carry ids** in these files and their sources, for example the `// [#20150]` block at `import-mapping-target.test.ts:17`, the `#4001 批 20` header of `object-strictness-batch20.test.ts` and `object.zod.ts:2532`. 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 eb23966 commit 124533b

17 files changed

Lines changed: 103 additions & 103 deletions

‎packages/spec/src/data/filter.test.ts‎

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ describe('ComparisonOperatorSchema', () => {
7474
// rather than an ISO refinement.
7575
// ==========================================================================
7676

77-
describe('string comparands (#5685)', () => {
77+
describe('string comparands — the ordering slots take a string, which is what the date macros emit', () => {
7878
const OPS = ['$gt', '$gte', '$lt', '$lte'] as const;
7979

8080
/** `resolveFilterTokens` returns `asYmd(...)` for every calendar-day token. */
@@ -163,7 +163,7 @@ describe('ComparisonOperatorSchema', () => {
163163
// `filter-comparand-shape.test.ts`.
164164
// ==========================================================================
165165

166-
describe('null comparand (#14080)', () => {
166+
describe('null comparand — refused in the four ordering slots, like a null list member', () => {
167167
const OPS = ['$gt', '$gte', '$lt', '$lte'] as const;
168168
const messagesOf = (result: { error?: { issues: Array<{ message: string }> } }): string =>
169169
(result.error?.issues ?? []).map((i) => i.message).join('\n');
@@ -194,7 +194,7 @@ describe('ComparisonOperatorSchema', () => {
194194
expect(nested.success).toBe(false);
195195
});
196196

197-
it('the null PREDICATES are untouched — $eq: null / $ne: null keep parsing (#5332)', () => {
197+
it('the null PREDICATES are untouched — $eq: null / $ne: null keep parsing', () => {
198198
expect(FieldOperatorsSchema.safeParse({ $eq: null }).success).toBe(true);
199199
expect(FieldOperatorsSchema.safeParse({ $ne: null }).success).toBe(true);
200200
// The normalized AST is a logical group at its root (a bare field
@@ -256,7 +256,7 @@ describe('SetOperatorSchema', () => {
256256
// on a missing prescription is not coverage of this ruling.
257257
// ==========================================================================
258258

259-
describe('$field members are refused (#7596)', () => {
259+
describe('$field members are refused', () => {
260260
it('refuses a $field member of $in, naming the index and the alternative', () => {
261261
const result = SetOperatorSchema.safeParse({ $in: ['won', { $field: 'budget' }] });
262262
expect(result.success).toBe(false);
@@ -295,7 +295,7 @@ describe('SetOperatorSchema', () => {
295295
expect(SetOperatorSchema.safeParse({ $in: [] }).success).toBe(true);
296296
});
297297

298-
it('refuses a null member of $in / $nin — ruled 2026-08-31 (#13357)', () => {
298+
it('refuses a null member of $in / $nin — ruled 2026-08-31', () => {
299299
for (const op of ['$in', '$nin'] as const) {
300300
const result = SetOperatorSchema.safeParse({ [op]: ['won', null] });
301301
expect(result.success, op).toBe(false);
@@ -361,7 +361,7 @@ describe('RangeOperatorSchema', () => {
361361
// parse verdicts on a declaration surface, not runtime refusals.)
362362
// ==========================================================================
363363

364-
describe('string endpoints (#6571)', () => {
364+
describe('string endpoints — `$between` takes the ISO and clock strings the platform produces', () => {
365365
/**
366366
* `resolveFilterTokens`' `walk` descends into arrays, and every branch of
367367
* the resolver returns a string, so a token range resolves to two strings:
@@ -469,7 +469,7 @@ describe('RangeOperatorSchema', () => {
469469
// was.
470470
// ==========================================================================
471471

472-
describe('$field endpoints are refused (#7596)', () => {
472+
describe('$field endpoints are refused', () => {
473473
it('refuses a $field LOWER bound, naming index 0 and the alternative', () => {
474474
const result = RangeOperatorSchema.safeParse({
475475
$between: [{ $field: 'a.min' }, '2026-12-31'],
@@ -546,7 +546,7 @@ describe('RangeOperatorSchema', () => {
546546
* always rejected both), and the pair above them is what moved — which is
547547
* exactly the evidence that the two faces now agree.
548548
*/
549-
it('the whole-filter face refuses these field conditions too — #7711 (was green pre-#7711)', () => {
549+
it('the whole-filter face refuses these field conditions too — green while the group branch was a non-strict catch-all', () => {
550550
const withReference = NormalizedFilterSchema.safeParse({
551551
$and: [{ amount: { $between: [{ $field: 'budget' }, 100] } }],
552552
});
@@ -573,7 +573,7 @@ describe('RangeOperatorSchema', () => {
573573
* is the SCALAR comparand, and it is also what the refusal above prescribes.
574574
* If this went red the refusal message would be sending authors nowhere.
575575
*/
576-
it('leaves the four ORDERING slots taking a reference — #5222, and the prescribed alternative', () => {
576+
it('leaves the four ORDERING slots taking a reference — a column-to-column comparison, and the prescribed alternative', () => {
577577
expect(ComparisonOperatorSchema.safeParse({ $gt: { $field: 'budget' } }).success).toBe(true);
578578
expect(FieldOperatorsSchema.safeParse({
579579
$gte: { $field: 'a.min' }, $lte: { $field: 'a.max' },
@@ -605,7 +605,7 @@ describe('RangeOperatorSchema', () => {
605605
// (`filter-comparand-shape.test.ts`).
606606
// ==========================================================================
607607

608-
describe('blank $between endpoints are refused (#18012)', () => {
608+
describe('blank $between endpoints are refused', () => {
609609
const issuesOf = (result: { error?: { issues: Array<{ path: PropertyKey[]; message: string }> } }) =>
610610
result.error?.issues ?? [];
611611

@@ -676,7 +676,7 @@ describe('RangeOperatorSchema', () => {
676676
* face about each slot, so both are refused on save, at the endpoint, in the
677677
* sentence the enforced operator slot prints for the same pair.
678678
*/
679-
it('is refused by FilterConditionSchema too, at the endpoint, in the operator slot\'s words (#20116)', () => {
679+
it('is refused by FilterConditionSchema too, at the endpoint, in the operator slot\'s words', () => {
680680
const blank = FilterConditionSchema.safeParse({ age: { $between: [18, ''] } });
681681
expect(blank.success).toBe(false);
682682
expect(issuesOf(blank).map((i) => i.path)).toEqual([['age', '$between', 1]]);
@@ -1069,7 +1069,7 @@ describe('TypeScript Type System', () => {
10691069
* (the second reads `undefined`, not `never`: the old guard's `never` meets
10701070
* the slot's own `?`, and an optional `never` IS `undefined`.)
10711071
*/
1072-
it('accepts an ISO string on a Date field and orders string fields (#5685)', () => {
1072+
it('accepts an ISO string on a Date field and orders string fields', () => {
10731073
interface Deal {
10741074
close_date: Date; // resolved date macro arrives as 'YYYY-MM-DD'
10751075
shift_start: string; // Field.time — 'HH:MM[:SS[.fff]]'
@@ -1098,7 +1098,7 @@ describe('TypeScript Type System', () => {
10981098
* recorded: the old guard's `never` meets the slot's own `?`, and an optional
10991099
* `never` IS `undefined`.)
11001100
*/
1101-
it('accepts a resolved macro range on a Date field and ranges string fields (#6571)', () => {
1101+
it('accepts a resolved macro range on a Date field and ranges string fields', () => {
11021102
interface Deal {
11031103
close_date: Date; // a resolved token range arrives as two 'YYYY-MM-DD'
11041104
shift_start: string; // Field.time — 'HH:MM[:SS[.fff]]'
@@ -1459,7 +1459,7 @@ describe('NormalizedFilterSchema', () => {
14591459
// as well: the catch-all returned `{}` for the member it admitted, so the
14601460
// parse output no longer carried the condition it was asked about.
14611461
// ==========================================================================
1462-
describe('#7711 — a member the field-condition branch refuses has nowhere to land', () => {
1462+
describe('a member the field-condition branch refuses has nowhere to land', () => {
14631463
/** The card's clean control: `$null` has never been anything but a boolean. */
14641464
it('rejects a $and member whose operator map FieldOperatorsSchema refuses', () => {
14651465
const result = NormalizedFilterSchema.safeParse({
@@ -1538,7 +1538,7 @@ describe('NormalizedFilterSchema', () => {
15381538
* the field-condition branch as an empty record, which is what lets the
15391539
* group branch be `.strict()` without touching any of them.
15401540
*/
1541-
it('leaves the #5322 empty-combinator identities accepted', () => {
1541+
it('leaves the empty-combinator identities accepted — each reduces to its boolean unit', () => {
15421542
for (const identity of [{}, { $and: [] }, { $or: [] }, { $or: [{}] }, { $not: {} }]) {
15431543
expect(NormalizedFilterSchema.safeParse(identity).success).toBe(true);
15441544
}
@@ -1848,7 +1848,7 @@ describe('VALID_AST_OPERATORS', () => {
18481848
// corpus and the memory evaluator's pins — not here.
18491849
// ============================================================================
18501850

1851-
describe('FieldReferenceSchema.addDays (#14104)', () => {
1851+
describe('FieldReferenceSchema.addDays — a whole-day offset on a reference, an integer or another column', () => {
18521852
const firstIssue = (result: { error?: { issues: Array<{ code: string; path: PropertyKey[]; message: string }> } }) =>
18531853
result.error?.issues[0];
18541854

@@ -1959,7 +1959,7 @@ describe('FieldReferenceSchema.addDays (#14104)', () => {
19591959
expect(firstIssue(enforced)?.message.startsWith('addDays must be an integer or a { "$field" } reference')).toBe(true);
19601960
});
19611961

1962-
it('adds no LIST position — a reference carrying addDays is refused where a bare one is (#7596)', () => {
1962+
it('adds no LIST position — a reference carrying addDays is refused where a bare one is', () => {
19631963
const result = SetOperatorSchema.safeParse({ $in: [{ $field: 'due_date', addDays: 1 }] });
19641964
expect(result.success).toBe(false);
19651965
expect(firstIssue(result)?.path).toEqual(['$in', 0]);
@@ -1995,7 +1995,7 @@ describe('FieldReferenceSchema.addDays (#14104)', () => {
19951995
* and refuses `.` and `/` alike, so a slash-separated or unbackticked
19961996
* respelling trips it too.
19971997
*/
1998-
describe('filter.zod.ts docblock @examples (#16923)', () => {
1998+
describe('filter.zod.ts docblock @examples — a `$field` comparand names a column of the same row', () => {
19991999
const HERE = dirname(fileURLToPath(import.meta.url));
20002000
const SOURCE = readFileSync(resolve(HERE, 'filter.zod.ts'), 'utf8');
20012001

‎packages/spec/src/data/form-delete-behavior-options.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ const FORMS = [
175175

176176
// ────────────────────────────────────────────────────────────────────────────
177177

178-
describe('#11410 — `deleteBehavior` is never offered a value the schema refuses', () => {
178+
describe('`deleteBehavior` is never offered a value the schema refuses', () => {
179179
describe('the derived option source, which is why an explicit list is required', () => {
180180
it('offers all three values, and defaults to the one a master_detail refuses', () => {
181181
const node = derivedDeleteBehaviorNode();

‎packages/spec/src/data/form-return-type-options.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ function formulaFieldWith(returnType: string): Record<string, unknown> {
144144

145145
// ────────────────────────────────────────────────────────────────────────────
146146

147-
describe('#19677 — `returnType` is never offered a value the schema refuses', () => {
147+
describe('`returnType` is never offered a value the schema refuses', () => {
148148
it('derives a non-empty closed member set from FieldSchema', () => {
149149
// The floor for every assertion below: an empty or unreadable derivation
150150
// would make the set-equality checks vacuously true.

‎packages/spec/src/data/hook-body.test.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ describe('HookBody', () => {
6262
// #4391. These assertions are what must go red if any of them comes back
6363
// WITHOUT an implementation behind it.
6464

65-
it('does not offer `crypto.hash` as a capability token (#4391)', () => {
65+
it('does not offer `crypto.hash` as a capability token', () => {
6666
expect(HookBodyCapability.options).toEqual([
6767
'api.read',
6868
'api.write',
@@ -73,7 +73,7 @@ describe('HookBody', () => {
7373
expect(HookBodyCapability.options).not.toContain('crypto.hash');
7474
});
7575

76-
it('rejects a body declaring `crypto.hash`, with the retirement prescription (#4391)', () => {
76+
it('rejects a body declaring `crypto.hash`, with the retirement prescription', () => {
7777
const r = ScriptBodySchema.safeParse({
7878
language: 'js',
7979
source: "ctx.input.fp = await ctx.crypto.hash('sha256', ctx.input.email);",
@@ -90,7 +90,7 @@ describe('HookBody', () => {
9090
expect(message).toMatch(/Delete the capability/s);
9191
});
9292

93-
it('still accepts `crypto.uuid` — the sibling that IS implemented (#4391)', () => {
93+
it('still accepts `crypto.uuid` — the sibling that IS implemented', () => {
9494
const r = ScriptBodySchema.safeParse({
9595
language: 'js',
9696
source: 'ctx.input.trace = ctx.crypto.randomUUID();',
@@ -99,7 +99,7 @@ describe('HookBody', () => {
9999
expect(r.success).toBe(true);
100100
});
101101

102-
it("gives an UNKNOWN token zod's own message, not the retirement one (#4391)", () => {
102+
it("gives an UNKNOWN token zod's own message, not the retirement one", () => {
103103
// Only the value that used to be legal gets "was removed" — telling the
104104
// author of a typo that their token was retired would misinform.
105105
const r = ScriptBodySchema.safeParse({

‎packages/spec/src/data/hook.test.ts‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ describe('HookEvent', () => {
3333
});
3434
});
3535

36-
describe('Removed non-dispatched events (#3195)', () => {
36+
describe('Removed non-dispatched events', () => {
3737
it('should reject per-method read and *Many events that the engine never dispatched', () => {
3838
// These were declared but never fired; the engine only ever triggers the
3939
// 8 events above. Removed rather than left as silent no-ops — read
@@ -349,7 +349,7 @@ describe('HookSchema', () => {
349349
// that the VALUE set is closed (a non-member is a VALUE error located at
350350
// `runAs`, never a top-level unrecognized key).
351351
// ---------------------------------------------------------------------------
352-
describe('runAs (#14010)', () => {
352+
describe('runAs — a hook may run as `system` or `user`, and inherits by default', () => {
353353
const base = {
354354
name: 'stamp_grade',
355355
object: 'account',
@@ -526,7 +526,7 @@ describe('HookContextSchema', () => {
526526
// it — a parsed context would lose the key and every guard downstream would
527527
// read undefined. The populate and sandbox halves are pinned where they can
528528
// execute (objectql's cascade suite; runtime's QuickJS integration pin).
529-
describe('Referential-Cleanup Marker (#13644)', () => {
529+
describe('Referential-Cleanup Marker — declared, and true only on a reference-cleanup write by the engine', () => {
530530
it('keeps referentialFieldClear through a parse, typed — the guard idiom evaluates on the parsed value', () => {
531531
const context = HookContextSchema.parse({
532532
object: 'note',
@@ -724,7 +724,7 @@ describe('HookContextSchema', () => {
724724
// #3280 made `organizationId` the blessed developer-facing name; the
725725
// `tenantId` alias was removed from this surface in v16 (#3290). A stray
726726
// `tenantId` key is now stripped by the schema rather than surfaced.
727-
it('exposes session.organizationId and no longer carries the removed tenantId alias (#3290)', () => {
727+
it('exposes session.organizationId and no longer carries the removed tenantId alias', () => {
728728
const context = HookContextSchema.parse({
729729
object: 'account',
730730
event: 'beforeInsert',
@@ -921,7 +921,7 @@ describe('HookSchema - condition property', () => {
921921
// defineHook factory (#4269)
922922
// ============================================================================
923923

924-
describe('defineHook (#4269)', () => {
924+
describe('defineHook — the authoring factory, so a hook is validated where it is written', () => {
925925
const config: Hook = {
926926
name: 'order_guard',
927927
object: 'order',
@@ -1003,7 +1003,7 @@ describe('defineHook (#4269)', () => {
10031003
* TS2578 "Unused '@ts-expect-error' directive" on the two directives below.
10041004
* Direction predicted before running it, and that is what it did.
10051005
*/
1006-
describe('session.roles retirement (#5050, ADR-0049)', () => {
1006+
describe('session.roles retirement (ADR-0049)', () => {
10071007
it('REJECTS an authored `roles`, with the prescription in the message', () => {
10081008
expect(() =>
10091009
HookContextSchema.parse({
@@ -1131,7 +1131,7 @@ describe('session.roles retirement (#5050, ADR-0049)', () => {
11311131
* `session.positions.includes(...)` as an access check is exactly what the
11321132
* `roles` tombstone above was written to stop.
11331133
*/
1134-
describe('session.positions / session.preserveAudit declaration (#5605)', () => {
1134+
describe('session.positions / session.preserveAudit declaration', () => {
11351135
it('PRESERVES `positions` through a parse instead of stripping it', () => {
11361136
const context = HookContextSchema.parse({
11371137
object: 'account',
@@ -1145,7 +1145,7 @@ describe('session.positions / session.preserveAudit declaration (#5605)', () =>
11451145
expect(context.session?.positions).toEqual(['sales_manager', 'org_admin']);
11461146
});
11471147

1148-
it('PRESERVES `preserveAudit` through a parse (#3493 has a live consumer)', () => {
1148+
it('PRESERVES `preserveAudit` through a parse (the opt-in a historical import uses to keep its audit stamps)', () => {
11491149
const context = HookContextSchema.parse({
11501150
object: 'account',
11511151
event: 'beforeInsert',
@@ -1283,7 +1283,7 @@ describe('session.positions / session.preserveAudit declaration (#5605)', () =>
12831283
* because a green companion test read as a pin is how #5605's third assertion
12841284
* nearly got over-credited.
12851285
*/
1286-
describe('HookContext.api typing (#5945)', () => {
1286+
describe('HookContext.api typing — the minimum scoped context the docs teach: `object()` and `transaction()`', () => {
12871287
/** Shaped like ObjectQL's `ScopedContext` — a live object, not authored data. */
12881288
const liveApi = {
12891289
object: (_name: string) => ({
@@ -1379,7 +1379,7 @@ describe('HookContext.api typing (#5945)', () => {
13791379
// old `timeout` is a retiredKey tombstone on this strict shape, so the
13801380
// rejection carries the RENAME (the prescription is the payload) rather than a
13811381
// bare unrecognized-key error, and the value survives at the same magnitude.
1382-
describe('hook.timeout → hook.timeoutMs (#14478, ADR-0087 `hook-timeout-to-timeout-ms`)', () => {
1382+
describe('hook.timeout → hook.timeoutMs (ADR-0087 `hook-timeout-to-timeout-ms`)', () => {
13831383
const base = { name: 'audit_order', object: 'order', events: ['afterInsert' as const], handler: 'auditOrder' };
13841384

13851385
it('REFUSES the retired `timeout` spelling with the rename in the message', () => {

‎packages/spec/src/data/import-coercion.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
} from './import-coercion';
2121
import { REFERENCE_VALUE_TYPES } from './field-value.zod';
2222

23-
describe('import boolean tokens (#4173)', () => {
23+
describe('import boolean tokens — one table for the server coercion and the Import Wizard preview', () => {
2424
it('both sets are non-empty and disjoint', () => {
2525
expect(IMPORT_BOOLEAN_TRUE_TOKENS.size).toBeGreaterThan(0);
2626
expect(IMPORT_BOOLEAN_FALSE_TOKENS.size).toBeGreaterThan(0);
@@ -45,7 +45,7 @@ describe('import boolean tokens (#4173)', () => {
4545
});
4646
});
4747

48-
describe('import reference types (#4173)', () => {
48+
describe('import reference types — exported from spec, not copied at each consumer', () => {
4949
it('is the reference value types plus the legacy generic spelling, exactly', () => {
5050
expect([...IMPORT_REFERENCE_TYPES].sort()).toEqual(
5151
[...REFERENCE_VALUE_TYPES, 'reference'].sort(),

‎packages/spec/src/data/import-mapping-target.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ describe('unknownImportMappingTargets', () => {
208208
)).toEqual([]);
209209
});
210210

211-
it('[#20149] is empty for a mapping that writes an address by its declared parts — every transform, split elements too', () => {
211+
it('is empty for a mapping that writes an address by its declared parts — every transform, split elements too', () => {
212212
expect(unknownImportMappingTargets(
213213
[
214214
{ source: 'Name', target: 'full_name' },
@@ -221,7 +221,7 @@ describe('unknownImportMappingTargets', () => {
221221
)).toEqual([]);
222222
});
223223

224-
it('[#20149] refuses a part of a field the same mapping also writes whole, naming where the whole value is written', () => {
224+
it('refuses a part of a field the same mapping also writes whole, naming where the whole value is written', () => {
225225
const misses = unknownImportMappingTargets(
226226
[
227227
{ source: 'Street', target: 'mailing_address.street' },

0 commit comments

Comments
 (0)