Skip to content

Commit a3bd157

Browse files
test(spec): the last ui/ file group's test titles state each cited decision in words instead of a tracker number (stage 23) (#21947)
Part of #20749 Clause-②: no Stage 23 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 last name-ordered `ui/` group: the 16 id-bearing test files directly under `packages/spec/src/ui/` from `view-item-config-type.test.ts` to `widget.test.ts`. Those files carried 100 messages and 106 tracker ids, citing 53 records. All 106 now either state what their record decided, in words (form D), or are dropped where the title already says it. No needle sits in this group. Text only: no assertion, identifier, test count or code comment changes, and no file is renamed. ## Census at the base (`9e33ee7c59`) Instruments: `census10.cjs` (md5 `9d08602ab972b4b8643c90d64d40fa41`), `census.cjs` (md5 `6e42a45a926d375013c32d62f16a296e`), `census-wide.cjs` (md5 `c98410a19529c439adb0afbfb00026a2`) and `dirtable.cjs` (md5 `dda605c54745b4a60cc14c9a686e4eff`), byte-identical to the copies stages 10 to 22 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 worktree was cut from `origin/main` at `9e33ee7c59`, the claim's base. Both instruments read **571 messages / 604 ids in 127 files**, the seat's reading and stage 22's head reading. | directory | files | messages / ids | titles | other | |:--|--:|--:|--:|--:| | `api/` | 40 | 189 / 201 | 181 / 193 | 8 / 8 | | `system/` | 34 | 154 / 167 | 128 / 138 | 26 / 29 | | (files directly in `src/`) | 30 | 118 / 120 | 117 / 119 | 1 / 1 | | `ui/` (this PR: 16 of the 21 files) | 21 | 107 / 113 | 97 / 103 | 10 / 10 | | `ai/` | 1 | 2 / 2 | 0 | 2 / 2 | | `contracts/` | 1 | 1 / 1 | 0 | 1 / 1 | | **total** | **127** | **571 / 604** | **523 / 553** | **48 / 51** | The group reads **100 messages / 106 ids in 16 files**, the seat's figures file for file: | file (under `ui/`) | messages / ids | titles | other | |:--|--:|--:|--:| | `view-item-config-type.test.ts` | 1 / 1 | 1 / 1 | 0 | | `view-metadata-schema.test.ts` | 8 / 8 | 8 / 8 | 0 | | `view-metadata-type.test.ts` | 2 / 2 | 2 / 2 | 0 | | `view-overlay-options-bag.test.ts` | 6 / 6 | 6 / 6 | 0 | | `view-overlay-options-type.test.ts` | 1 / 1 | 1 / 1 | 0 | | `view-overlay-owner-hidden-retirement.test.ts` | 1 / 1 | 1 / 1 | 0 | | `view-overlay-viewkind-arm.test.ts` | 10 / 10 | 10 / 10 | 0 | | `view-overlay-viewkind-type.test.ts` | 1 / 1 | 1 / 1 | 0 | | `view-strictness-batch18.test.ts` | 10 / 11 | 10 / 11 | 0 | | `view-submit-redirect-url.test.ts` | 5 / 5 | 5 / 5 | 0 | | `view-union-branch-focus.test.ts` | 7 / 7 | 6 / 6 | 1 / 1 | | `view-union-diagnostics.test.ts` | 4 / 5 | 4 / 5 | 0 | | `view-union-retirement-prescription.test.ts` | 1 / 1 | 1 / 1 | 0 | | `view.test.ts` | 39 / 43 | 38 / 42 | 1 / 1 | | `widget-i18n-retirement.test.ts` | 3 / 3 | 2 / 2 | 1 / 1 | | `widget.test.ts` | 1 / 1 | 1 / 1 | 0 | | **16 files** | **100 / 106** | **97 / 103** | **3 / 3** | Three more test files sit in the same name range and carry no id (`view-item-owner-hidden-retirement.test.ts`, `view-list-tabs-retirement.test.ts`, `vocabulary-derivation.test.ts`). The three "other" strings are rewritten and declared to the text-only tool: the table label at `view-union-branch-focus.test.ts:139`, which prints inside two `for … of` test titles, and the expect messages at `view.test.ts:4099` and `widget-i18n-retirement.test.ts:135`. - **Controls.** Lit: `ui/notification.test.ts` and `api/api-error-code-type.test.ts`, outside the group, read 1 id each at the base and at the head. Dark: `view.test.ts` reads 0 at the head while 92 of its comment lines still carry a number. Planted in a scratch tree: an id put into a `widget.test.ts` title reads 1 / 1 (`title:describe`), and an id put into a `view-metadata-type.test.ts` comment reads 0. - **A wider pattern** (any `#` plus digits) reads the same as the gate pattern in 15 of the 16 files at the base. `view.test.ts` reads 2 more, and keeps them at the head: the CSS colours `'#00cc00'` (`:2770`) and `'#22c55e'` (`:3537`), fixture values that cite nothing. - **At the head:** 471 messages / 498 ids in 111 files. The 16 files read 0 / 0, `ui/` reads 7 / 7, and no other file moved. ## How the area was chosen `ui/` has no subdirectory test file with an id, so it is taken in name-ordered file groups near the ~100-id bound. Stage 22's re-cut named this group at 106 ids, and this census reads 106, so no re-cut was needed. `view.test.ts` (43 ids) is one file inside one text-only proof here, so it is not split. **`ui/` after this PR** reads 7 / 7, all kept items: stage 20's `component-props-unknown-members.pin.test.ts:322`, stage 21's four colour literals (`dashboard-chart-structure-refusal.test.ts:94`, `dashboard.test.ts:124`), and stage 22's two needles (`notification.test.ts:123`, `strictness-batch14.test.ts:395`). **Named for the next stages** (cut from the head census, 471 / 498): - **`api/`, 201 ids in 40 files**, with no subdirectory. Its first name-ordered group near the bound is `ai-agents-envelope.test.ts` through `package-lifecycle.test.ts`: 27 files, 100 messages / 106 ids (95 / 101 titles, 5 / 5 other: `auth.test.ts`, `discovery-environment-subset.pin.test.ts` and three in `export-job-family-retirement.test.ts`). The second is `plugin-rest-api.handler-status-retirement.test.ts` through `zod-issues-to-fields.test.ts`: 13 files, 89 / 95, `protocol.test.ts` alone 50. - `system/` 167, two stages. The files directly in `src/`, 120, one. - The needles: the three docblock needles, the kept `:322` and stage 22's two. One stage, with an at-tier review. ## What each id became - **25 literals (27 ids)** now state a decision in words. - **20 literals (22 ids)** get their subject back in words, where the number stood for a thing. - **55 literals (57 ids)** drop a number the title already explains. Every cited record was fetched with all its comments through REST, and its decision was read from its ruling, ACCEPT and landing comments: 53 records, 51 answer 200 and 2 answer 404. Five citations are objectui's and were read from objectui: `objectui#5233`, `objectui#2231` (cited bare at `view-strictness-batch18.test.ts:309`), `objectui#6237` (cited bare at `view.test.ts:949`), `objectui#5435` and `objectui#3289`. Three same-number records in the other repository were fetched first and set aside: `objectstack#2231` is a version-packages PR, `objectstack#6237` a datasource PR, and `objectui#2998` a form PR; `framework#1894 / #2998` are this repository's #1894 and #2998 under its old name. The two that answer 404 were read from what landed: - **#9933**, from its landing commit `d5552ca13f` ("admit columnState as an explicitly runtime-only view-overlay key") and the CHANGELOG entry for `d5552ca`; - **#11195**, from PR #11458, the PR that closed it ("UserActionsConfigSchema adopts group / hideFields / rowColor (ruled A on objectui#5435)"). One citation names the wrong record, and the titles now state what landed instead. `#3896 close-out` (twice in `view.test.ts`) names #3896, the sharing-rule criteria card, which records no decision about these keys; the two titles state the decision from the landed tombstones of `form.defaultSort` and `view.responsive` / `view.performance`, as stage 20 did for `action.test.ts`. Where a record's first decision was corrected later, the title follows the correction: - **#6926:** its first triage direction retired the `groups` alias; the measurement found live consumers, and the maintainer re-ruled A, a fold at the producer. The two titles say "the producer-side `groups` fold" and "folds onto `sections`". - **#7025 and #7741:** #7025 froze the acceptance face; #7741's ruling then moved it, and later retirements moved it again, each pinned. The title says "frozen by the diagnostics work; every move since is deliberate and pinned", not "moved only once". - **#7510:** the "[#7510] ⛔ the acceptance face did not move" describe sits beside two ruled moves recorded in its own comments, so the title now names what did not move it: "⛔ the branch focusing did not move the acceptance face". **Stated in words:** | record | literal (under `ui/`) | now reads | the decision | |:--|:--|:--|:--| | #5599 | `view-metadata-schema.test.ts:95` | "REJECTS a bare `{}` — the pin this line used to make, reversed by the identity precondition" | Maintainer ruling 2026-08-06, direction B: a minimal identity precondition ahead of the union's four members; each member's `.strip()` is untouched. | | #7741 | `view-metadata-schema.test.ts:125` | "… NO object binding — a row no read path could serve, with located guidance" | Maintainer ruling 2026-08-12, direction B: a row that cannot be expanded or served by any read path is not stored and badged valid; the inline arm requires the binding, refused with `defineView`'s guidance. | | #5599 | `view-metadata-schema.test.ts:215` | "identity precondition — a body must read as a view before any member judges it" | The same direction B. | | `objectui#5233` | `view-metadata-schema.test.ts:413` | "… a `columnState`-only patch (the patch-only write the console persists)" | Maintainer ruling 2026-08-12 (on #7494): `persistViewPatch` stores the patch only, not the merged base. | | #17152 | `view-overlay-owner-hidden-retirement.test.ts:335` | "… names the family's D2 conversion (ruled: a D3 entry per family, even beside a lossless D2)" | Ruling B (director seat, 2026-09-10, upheld 2026-09-11): one D3 semantic entry per retired family, beside its D2 conversion even when D2 is lossless. | | #7494 | `view-overlay-viewkind-arm.test.ts:102` | "the console %s toggle (a patch-only write, as ruled) is ACCEPTED on listOverlay" | Maintainer ruling 2026-08-12: the overlay store is org-wide, and the toolbar write stores the patch only. | | #4001 | `view-strictness-batch18.test.ts:91` | "批 18, unknown keys refused — the doors these shapes are reachable through" | The strictness campaign: an unknown key on the authorable surface is refused, not silently stripped. | | #15469 | `view-strictness-batch18.test.ts:149` | "… a CLOSED entry, and since the renderer-ahead `.passthrough()` was removed a CLOSED parent too" | Maintainer ruling A (decision batch #41, 2026-09-05): every key the gantt and tree renderers read is declared, and both `.passthrough()` calls go. | | #5074 | `view-strictness-batch18.test.ts:364` | "[RESOLVED by the ruled split] ViewItemSchema SPLIT — …" | Maintainer ruling A (2026-08-04): split — a strict authoring `ViewItemSchema` and a reopened wire member in the union. | | #5074 | `view-strictness-batch18.test.ts:403` | "[RESOLVED with the ruled split] ListViewSchema.sort CLOSED — …" | The split's scope addendum: the wire door strips the console's decoration keys before validating, so `sort[]` closed again with no declared `id`. | | #7025, #7741 | `view-union-diagnostics.test.ts:246` | "the acceptance face of ViewMetadataSchema — frozen by the diagnostics work; every move since is deliberate and pinned" | #7025's sweep rule: the diagnostic face improves, the acceptance face does not move; #7741's ruled binding requirement is the first pinned move since. | | #9463 | `view.test.ts:342` | "viewMode — the granularities the gantt renderer honours, measured" | Declare `viewMode` with exactly the granularities objectui's `GanttView` honours, measured, not invented (the spec half of objectui#5074's ruling). | | #17053 | `view.test.ts:441` | "the legacy string `sort` clause is retired — one spelling, the array" | objectui's ruling (director batch #77, option B): one spelling, the array; the spec stops producing the string. | | #13704 | `view.test.ts:873` | "wizard tightening — sections are the steps, the inert step keys are refused, no key is added" | The ruled shape of #13622 (2026-08-31): sections are the steps, the wizard-inert step keys are refused at parse, zero new keys. | | `objectui#6237` | `view.test.ts:949` | "… stay accepted on tabbed/simple (the ruled split confines it to wizard steps)" | Maintainer ruling 2026-08-30 (director batch #3): `FormSectionConfig` is split, so tabbed sections take a predicate and wizard steps carry none. | | `objectui#2231` | `view.test.ts:2876` | "ListColumnSchema summary object form and prefix — spec-owned, no longer an objectui-local extension" | The derive-by-reference unification: `677b591` moved `prefix` and the `{ type, field }` `summary` form into the spec, closing objectui's local `.extend()`. | | #3896 (see above) | `view.test.ts:3144` | "FormViewSchema — retired defaultSort (audit close-out: nothing read it)" | The landed tombstone: `form.defaultSort` was removed because nothing read it. | | `objectui#5435` | `view.test.ts:3386` | "… defaults asymmetry, copied from what the renderer reads" | Ruling A (2026-08-22): the spec adopts `group` / `hideFields` / `rowColor`, with the defaults copied from `ListView`'s reads. | | #3896 (see above) | `view.test.ts:3826` | "ListViewSchema — retired responsive/performance (audit close-out: no renderer read them)" | The landed tombstones: no renderer or runtime read either key. | | #7176 | `view.test.ts:3847` | "ListViewSchema — retired striped/bordered/virtualScroll (every reader only passed them through)" | Maintainer ruling 2026-08-10: retire under ADR-0049, since every measured reader copied the keys forward and none applied them. | | #5832 | `view.test.ts:4099` (expect message) | "`HttpMethodType` was renamed to `HttpMethodSubset`" | Maintainer ruling 2026-08-06: rename the 5-value subset; the 7-value `HttpMethod` keeps its name and its wire contract. | | #16577, #13817 | `view.test.ts:4708` | "… the `type: 'calendar'` axis is NOT gated by the `allowedVisualizations` check (ruled: a completeness warning)" | Ruling B (director seat, 2026-09-11): the #13817 guard keeps gating `allowedVisualizations` only; the `type: 'calendar'` route is carried at warning by `checkViewCompleteness`. | | #19228 | `view.test.ts:4793` | "view row bound — `pagination.pageSize` is the one bound; no per-kind `limit` on the view configs" | Maintainer ruling D (2026-09-23): one row bound per view, `pagination.pageSize`; the per-kind `limit` was removed before it shipped. | | #5055 | `widget-i18n-retirement.test.ts:70` | "ui/ widget + i18n family retirement — doorless vocabularies removed, not tightened" | Maintainer ruling A (2026-08-06): ADR-0049 enforce-or-remove retires the unreachable widget and locale vocabularies; closing them would only dress a dead slot as a checked one. | | `objectui#3289` | `widget-i18n-retirement.test.ts:196` | "the surviving `error` slot is exactly the one objectui renamed its own slot onto, with no alias" | objectui followed the spec: its widget `errorMessage` slot became the spec's `error`, with no alias, and the form renderer produces it. | **Subject back in words** (20 literals): "(binding pair, #7741)" becomes "(the object + viewKind binding pair)"; "union error behaviour (#5014)" becomes "union error behaviour (where a branch prescription gets buried)"; the five `#7496` prefixes become "the ruled redirect `url` shape —" (twice) and "ruled bullet 1 / 2 / 3 —", the file's own name for the ruling's three bullets; "the pre-#7510 ranking" becomes "the pre-fix ranking"; "the #4001 wrap prescription" becomes "the `defineView` wrap prescription"; the acceptance-face describe at `view-union-branch-focus.test.ts:261` (above); "the #6926 fold" becomes "the producer-side `groups` fold"; "(#7025 membership)" becomes "and the union corpus pins it accepted"; "(#8321/#12174)" becomes "(a negative or fractional scale)", what that test probes; "the exact declaration #9340 exists to make legal" and "the gap #9340 closes" name "this block"; "(acceptance criterion, objectstack#11195)" becomes "(the acceptance criterion for adopting the three keys)"; "(#7176 rides …)" becomes "(the retirement rides …)"; "the axis #13817 does not gate" becomes "the axis the `allowedVisualizations` check does not gate"; "zero holders after #5055" becomes "after the widget + i18n retirement"; "#5055 — the one surviving shape" becomes "the widget retirement — the one surviving shape". **Dropped where already stated** (55 literals, 57 ids). A number goes only where the title already says its decision. Examples: the four `[#19920]` prefixes ("… typed by its arm, not unknown", "… a parsed view body, not unknown") and `[#19871]`; the six `[#20051]` describes on the `options` bag and the one in `view-union-retirement-prescription.test.ts`; the eight `#20186` describes ("a column-less list PATCH is judged by the list member", "each member judges ONE viewKind", …); the three `[#6391]` describes, three `[#7510]` titles and the `[#21180]` table label ("the retired `publicPicker` key itself"); "(#3095)", "(#5074)" after "`.strip()` round-tripping is untouched", "(#9933)" after "runtime-only overlay key", and the `view.test.ts` tails `(#15469)`, `(#6926)`, `(#12174)`, `(#19088)`, `(#7084)`, `(#9340 — …)`, `(#17499)`, `(#18791)`, `(framework#1894 / #2998)`, `(#5073 — …)` x2, `(#8010)`, `[#4688]`, `[#4691]`, `(#6416 / #6619)`, `(#17063)`, `(#16885)`, `(#13817)` and `(#16577)`. The batch label `批 18` stays, in stage 20's "批 19, unknown keys refused" form on the file's first describe and bare on the other four. `W2` stays: the file's own header defines W1 and W2. The commit `ce70876e` stays in "(measured on origin/main ce70876)": a commit, not a tracker id. **No file is renamed.** ## Readers - **Test-name filters:** none. No tracked script, workflow or package config passes `-t` / `--testNamePattern` (the 3 hits are `docker build -t`, `type -t` and `lsof -t`). - **Snapshots:** none. No `__snapshots__` directory is tracked under `packages/spec`, and none of the 16 files calls a snapshot matcher. - **Projects:** none of the 16 files is in the `repo` project (`packages/spec/vitest.repo-tests.json`); all run in `local`. - **By substring:** every old literal, its id-bearing fragment and a window around each id (299 needles) was searched with `git grep` at the base, across the tracked tree outside its own file. No gate, doc, filter, snapshot, QA checklist entry or `scripts/check-*.mjs` self-test reads one. The 16 hits are windows that share wording with code comments and one CHANGELOG line: 15 comments in the migration registry, its semantic entries and `view-list-tabs-retirement.test.ts` read "(ruling B on #17152)", and `packages/spec/CHANGELOG.md:30342` reads "runtime-only overlay key (#9933)". - **Cross-references by id:** two places name the `columnState` section of `view-metadata-schema.test.ts` as "§#9933": a code comment at `packages/spec/scripts/strictness-ledger.test.ts:380` and the `view.zod.ts` row of `docs/audits/2026-07-unknown-key-strictness-ledger.md`. Neither matches a string; both point a reader at the section, which still carries "columnState — runtime-only overlay key" in its title and its banner comment. A code comment and an audit record are not this card's share, so neither is edited. ## 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 three lines: `view-union-branch-focus.test.ts:139`, `view.test.ts:4099` and `widget-i18n-retirement.test.ts:135`. - **Result:** 16 of 16 files SAME on all three legs, with the per-file counts predicted in writing before the run. - **Totals:** 100 changed string leaves in 100 literals: 97 titles and 3 declared. The diff's `+` and `-` lines are exactly the 100 planned lines as multisets, and every file keeps its line count. - **Controls (14 of 14 as predicted on the first run, 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; an `it.each` row given an id VIOLATION; an undeclared expect message changed VIOLATION; a title re-split into a `+` chain DIFF; a declared expect message reverted to base SAME; a declared expect message given a new id VIOLATION; the declared table label given a new id VIOLATION; a template-literal title given a new id VIOLATION. - **Templates and tables:** one `.each` title changes, `view-overlay-viewkind-arm.test.ts:102`, a `%s` template whose placeholder and rows are untouched. The table label at `view-union-branch-focus.test.ts:139` feeds two `for … of` template titles, which print it whole. The template-literal title at `:173` changes only its text before `${label}`. **Test counts:** the 16 files were run at the base, in a separate base worktree, and at the head, with `--project local --project repo`. Both sides read 862 tests in 16 files, all passed, with the same count and status sequence per file in 16 of 16. 574 full test names change, and each changed name equals the base name with the planned replacements applied (0 mismatches). No full name repeats on either side. ## Changeset: `skip-changeset` Measured, not assumed: - `npm pack --dry-run` of `@objectstack/spec` lists 2068 files. 0 of the 16 touched files are in it, and no `*.test.ts` at all. The controls `src/ui/view.zod.ts`, `src/ui/widget.zod.ts` and `dist/index.mjs` are in it. - In the built `dist/`, a new phrase and an old one each read in 0 files. The control `Unrecognized key` reads in 42. So this PR publishes nothing, and no changeset is added. ## Verification (at `75022207b3`) - `pnpm turbo run build` over all packages: 71 / 71, through the shared verify lock (`VERDICT command-exit 0`). - `@objectstack/spec`: - `vitest run --project local`: 619 files, 18471 passed, 1 todo. - `typecheck`: exit 0, including `check:test-typecheck` (52 files / 246 errors / 135 pinned signatures held). Its program holds all 16 group files, counted by path with `tsc --listFilesOnly -p tsconfig.test.json`. - `check:generated`: all 15 generated artifacts up to date, against the `dist/` the build above wrote. - **Gates:** `dispatch-gates --commands` derived 79 families, the same set as stage 22, 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 16 files reads 0 errors and 0 warnings. The population comes from ESLint's own config: 16 configured, 0 ignored. No file sets `parserOptions.project` or `projectService`, so no untouched file's verdict can move. - `check-governed-merges --test`: NOT governed, 200 changed lines (+100 / -100). - A control-byte scan over the 16 changed files finds none. ## `main` since the base Re-fetched just before this PR opened, `origin/main` was four commits past the base (`1f0469655f`: #21939, #21937, #21943, #21928). They touch 32 files, none of the 16; two are under `packages/spec` (a step-18 semantic migration entry and the migration registry, neither a test file). So `main` was not merged. The census on that tree still reads 571 / 604 in test files and 0 elsewhere. `git merge-tree` onto `1f0469655f` is clean, and none of the 8 open PRs touches any of the 16 files. ## Acceptance notes - **The `{{record.FIELD}}` title.** `view-submit-redirect-url.test.ts:187` keeps its literal placeholder after "ruled bullet 2 —"; this body spells it with `FIELD` because the platform strips angle-bracket fragments from PR text. - **Same-id test titles in this card's later stages** go with those stages: 5 lines in `packages/spec/src`, `api/api-error-code-type.test.ts:71` ("[#19920] …"), `stack.test.ts:1510` ("(#17063)"), `system/stack-server.test.ts:93` and `system/translation.test.ts:672` / `:767` ("(#4001)"). - **Same-id test titles in other packages** stay: 46 lines in 11 packages (`metadata-protocol` 11, `objectql` 8, `spec/scripts` 8, `lint` 6, `rest` 4, `plugin-auth` 3, `cli` 2, and one each in `plugin-security`, `plugin-sharing`, `qa/dogfood` and `service-automation`), each package's share under the #20513 lane children. The three `plugin-auth` titles cite objectstack's #5233, a different record from `objectui#5233`. - **Code comments with live ids** remain in these files and their sources, among them the "§#9933" cross-references above, the `[#7741]` / `[#21180]` corpus notes in `view-union-branch-focus.test.ts` and `view-union-diagnostics.test.ts`, and the `#5055` banners in `widget-i18n-retirement.test.ts`. Code comments are not this card's share. --- _Generated by [Claude Code](https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 3c7785d commit a3bd157

16 files changed

Lines changed: 100 additions & 100 deletions

‎packages/spec/src/ui/view-item-config-type.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ void [
7979
parsedArtifactScalarConfig,
8080
];
8181

82-
describe('[#19920] a ViewItem config is typed by its arm, not unknown', () => {
82+
describe('a ViewItem config is typed by its arm, not unknown', () => {
8383
it('each body typed through the published names parses at its door', () => {
8484
expect(ViewItemSchema.safeParse(listItem).success).toBe(true);
8585
expect(ViewItemSchema.safeParse(formItem).success).toBe(true);

‎packages/spec/src/ui/view-metadata-schema.test.ts‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { ListViewSchema, ViewItemSchema, ViewMetadataSchema } from './view.zod';
1818

1919
const PLACEHOLDER_DATA = { provider: 'object', object: 'crm_lead' } as const;
2020

21-
describe('ViewMetadataSchema — genuine validation across the three runtime shapes (#3095)', () => {
21+
describe('ViewMetadataSchema — genuine validation across the three runtime shapes', () => {
2222
// ── shape 2: standalone ViewItem record ───────────────────────────────────
2323
describe('ViewItem record form', () => {
2424
it('accepts a well-formed list ViewItem', () => {
@@ -92,7 +92,7 @@ describe('ViewMetadataSchema — genuine validation across the three runtime sha
9292
expect(ViewMetadataSchema.safeParse({ listViews: {} }).success).toBe(false);
9393
});
9494

95-
it('REJECTS a bare `{}` — the pin this line used to make, reversed by #5599', () => {
95+
it('REJECTS a bare `{}` — the pin this line used to make, reversed by the identity precondition', () => {
9696
// This assertion previously read `.toBe(true)`, justified as "legacy-
9797
// compatible … a truly empty body carries no viewKind/object, so every
9898
// consumer that filters on identity drops it". #5599 measured what that
@@ -122,7 +122,7 @@ describe('ViewMetadataSchema — genuine validation across the three runtime sha
122122
expect(r.success).toBe(true);
123123
});
124124

125-
it('REJECTS a raw list config with NO object binding — the #7741 dead row, with located guidance', () => {
125+
it('REJECTS a raw list config with NO object binding — a row no read path could serve, with located guidance', () => {
126126
// ⚠️ Deliberate inversion (#7741, ruled 2026-08-12, direction B). This
127127
// test used to PIN acceptance of exactly this body ("adhoc PUT, no
128128
// registry entry to inherit from") — and QA run #7695 measured what that
@@ -212,7 +212,7 @@ describe('ViewMetadataSchema — genuine validation across the three runtime sha
212212
});
213213

214214
// ── #5599: the identity precondition, ahead of all four arms ──────────────
215-
describe('identity precondition (#5599)', () => {
215+
describe('identity precondition — a body must read as a view before any member judges it', () => {
216216
// The reproduction from the issue, verbatim. On `origin/main` every input
217217
// in this block was ACCEPTED and reduced to `{ type: 'simple' }` — member 4
218218
// (`FormViewSchema.extend(…).strip()`) both strips unknown keys and requires
@@ -384,7 +384,7 @@ describe('ViewMetadataSchema — genuine validation across the three runtime sha
384384
expect(ViewMetadataSchema.safeParse({ groupByField: 'stage' }).success).toBe(false);
385385
});
386386

387-
it('does NOT close the arms — `.strip()` round-tripping is untouched (#5074)', () => {
387+
it('does NOT close the arms — `.strip()` round-tripping is untouched', () => {
388388
// The ruling on #5599 kept every arm's `.strip()`: a body that speaks the
389389
// vocabulary still carries undeclared aux keys through without a 422.
390390
// This is the deliberate residue of the minimal fix, pinned so a later
@@ -403,14 +403,14 @@ describe('ViewMetadataSchema — genuine validation across the three runtime sha
403403
// authorable (objectui's `gridNonAuthorKeys` disposition). The two halves
404404
// below are the card's executable acceptance criterion; a change that flips
405405
// either one is reversing a ruling, not tidying a schema.
406-
describe('columnState — runtime-only overlay key (#9933)', () => {
406+
describe('columnState — runtime-only overlay key', () => {
407407
// The REAL payload, measured: objectui's `persistViewPatch` sends
408408
// `{ columnState: { order?, widths? }, _isOverride: true }` and
409409
// `normalizeViewMetadata` inherits `name`/`object`/`viewKind` from the
410410
// shadowed entry (#2555). Before commit d5552ca13 this body carried no declared key,
411411
// so the identity precondition 422'd it — the ruled patch-only write for
412412
// a column drag could not persist.
413-
it('HALF 1 — the overlay face accepts a `columnState`-only patch (the objectui#5233 payload)', () => {
413+
it('HALF 1 — the overlay face accepts a `columnState`-only patch (the patch-only write the console persists)', () => {
414414
const r = ViewMetadataSchema.safeParse({
415415
name: 'showcase_task.default',
416416
object: 'showcase_task',
@@ -421,7 +421,7 @@ describe('ViewMetadataSchema — genuine validation across the three runtime sha
421421
expect(r.success).toBe(true);
422422
});
423423

424-
it('HALF 1 — a baseline-less columnState patch is refused at the MEMBERS (binding pair, #7741), not the precondition', () => {
424+
it('HALF 1 — a baseline-less columnState patch is refused at the MEMBERS (the object + viewKind binding pair), not the precondition', () => {
425425
// `columnState` is vocabulary now, so the precondition stays inert; what
426426
// refuses the unbound body is the members' object+viewKind requirement.
427427
const r = ViewMetadataSchema.safeParse({ columnState: { order: ['name'] } });

‎packages/spec/src/ui/view-metadata-type.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ const parsedUndeclaredKey: ViewMetadataParsed = { type: 'grid', columns: ['name'
7878
const parsedScalar: ViewMetadataParsed = 42;
7979
void [parsedFromUnknown, parsedUndeclaredKey, parsedScalar];
8080

81-
describe('[#19871] ViewMetadata is a view body, not unknown', () => {
81+
describe('ViewMetadata is a view body, not unknown', () => {
8282
it('has a typed body for every member of the union', () => {
8383
expect(Object.keys(BODY_OF_EACH_MEMBER).sort()).toEqual([...VIEW_METADATA_BRANCHES].sort());
8484
});
@@ -92,7 +92,7 @@ describe('[#19871] ViewMetadata is a view body, not unknown', () => {
9292
}
9393
});
9494

95-
describe('[#19920] ViewMetadataParsed is a parsed view body, not unknown', () => {
95+
describe('ViewMetadataParsed is a parsed view body, not unknown', () => {
9696
for (const branch of VIEW_METADATA_BRANCHES) {
9797
it(`the ${branch} member's parse output and diagnoseViewMetadata's data are both ViewMetadataParsed`, () => {
9898
const memberOutput: ViewMetadataParsed = VIEW_METADATA_MEMBERS[branch].parse(BODY_OF_EACH_MEMBER[branch]);

‎packages/spec/src/ui/view-overlay-options-bag.test.ts‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ function under(issues: Issue[], prefix: PropertyKey[]): Array<Omit<Issue, 'path'
7777
.sort((a, b) => `${a.path}|${a.code}`.localeCompare(`${b.path}|${b.code}`));
7878
}
7979

80-
describe('[#20051] the bag declares exactly the list kinds, derived from the shape', () => {
80+
describe('the bag declares exactly the list kinds, derived from the shape', () => {
8181
it('declares one block per list-view kind that has a block — `grid` has none', () => {
8282
// A pin on the DERIVATION (`listViewKindBlocks`): the list is not
8383
// hand-maintained in the schema, so it is stated once here, where a new
@@ -88,7 +88,7 @@ describe('[#20051] the bag declares exactly the list kinds, derived from the sha
8888
});
8989
});
9090

91-
describe('[#20051] a direct and an `options`-wrapped out-of-contract key get the same refusal', () => {
91+
describe('a direct and an `options`-wrapped out-of-contract key get the same refusal', () => {
9292
it('the card\'s headline: `options.timeline.metaFields` is refused by name, as `timeline.metaFields` is', () => {
9393
const timeline = { startDateField: 'created_at', titleField: 'name', metaFields: ['region'] };
9494
const direct = listOverlayIssues(overlay({ type: 'timeline', timeline }));
@@ -136,7 +136,7 @@ describe('[#20051] a direct and an `options`-wrapped out-of-contract key get the
136136
});
137137
});
138138

139-
describe('[#20051] the bag itself is closed', () => {
139+
describe('the bag itself is closed', () => {
140140
it('`options.foo` is refused by name at `options`, not dropped', () => {
141141
const issues = listOverlayIssues(overlay({ options: { foo: 1 } }));
142142
const hit = issues.find((i) => i.code === 'unrecognized_keys');
@@ -157,7 +157,7 @@ describe('[#20051] the bag itself is closed', () => {
157157
});
158158
});
159159

160-
describe('[#20051] a legal `options.KIND` still parses and round-trips', () => {
160+
describe('a legal `options.KIND` still parses and round-trips', () => {
161161
it('CONTROL: the legacy `options.map` bag objectui pins is accepted and kept in the parse output', () => {
162162
// objectui `packages/app-shell/src/views/InterfaceListPage.mapConfig.test.tsx`
163163
// — "CONTROL: the legacy `options.map` bag is still forwarded on its own
@@ -205,7 +205,7 @@ describe('[#20051] a legal `options.KIND` still parses and round-trips', () => {
205205
});
206206
});
207207

208-
describe('[#20051] the form overlay cannot take the bag the list overlay refused', () => {
208+
describe('the form overlay cannot take the bag the list overlay refused', () => {
209209
it('a column-less, type-less body with a bad `options` bag is refused, not accepted as a form', () => {
210210
// Without the form overlay's `options` pin this body is ACCEPTED: the list
211211
// member refuses it (no `columns`, and the bag), the form member — which
@@ -252,7 +252,7 @@ describe('[#20051] the form overlay cannot take the bag the list overlay refused
252252
* `.strip()` used to drop it unread; with the save storing the parsed body it
253253
* would have vanished on a `200` instead.
254254
*/
255-
describe('[#20051] a ViewItem record carries no top-level `options` bag', () => {
255+
describe('a ViewItem record carries no top-level `options` bag', () => {
256256
const record = (extra: Record<string, unknown> = {}) => ({
257257
name: 'crm_lead.board',
258258
object: 'crm_lead',

‎packages/spec/src/ui/view-overlay-options-type.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ const numericBagArtifact: AssembledViewArtifact = { object: 'crm_lead', viewKind
8282
const numericBagParsedArtifact: AssembledViewArtifactParsed = { object: 'crm_lead', viewKind: 'list', options: { kanban: 42 } };
8383
void [typedBag, numericBagMetadata, numericBagParsedMetadata, numericBagArtifact, numericBagParsedArtifact];
8484

85-
describe('[#19920] the list overlay options bag carries each kind block', () => {
85+
describe('the list overlay options bag carries each kind block', () => {
8686
it('the runtime key set is the kind set the type names', () => {
8787
const bag = VIEW_METADATA_MEMBERS.listOverlay.shape.options.unwrap();
8888
expect(Object.keys(bag.shape).sort()).toEqual([...KIND_BLOCKS]);

‎packages/spec/src/ui/view-overlay-owner-hidden-retirement.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ describe('overlay owner/hidden retirement — ADR-0087 registration', () => {
332332
expect(MIGRATIONS_BY_MAJOR[18]!.conversionIds).toContain('view-overlay-owner-hidden-removed');
333333
});
334334

335-
it('the family carries ONE D3 semantic entry, and it names the family\'s D2 conversion (ruling B on #17152)', () => {
335+
it('the family carries ONE D3 semantic entry, and it names the family\'s D2 conversion (ruled: a D3 entry per family, even beside a lossless D2)', () => {
336336
const semantic = MIGRATIONS_BY_MAJOR[18]!.semantic;
337337
const family = semantic.filter((s) => s.id === 'view-overlay-owner-hidden-retired');
338338
expect(family).toHaveLength(1);

‎packages/spec/src/ui/view-overlay-viewkind-arm.test.ts‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ function acceptance(body: unknown) {
8080
return { data: parsed.data as Record<string, unknown>, branch: diagnosis.branch };
8181
}
8282

83-
describe('#20186 a column-less list PATCH is judged by the list member', () => {
83+
describe('a column-less list PATCH is judged by the list member', () => {
8484
it('the headline `{ name, object, viewKind: list, sort }` is ACCEPTED on listOverlay and parses to a LIST', () => {
8585
const { data, branch } = acceptance({ ...LIST, sort: [{ field: 'name', order: 'asc' }] });
8686
expect(branch).toBe('listOverlay');
@@ -99,14 +99,14 @@ describe('#20186 a column-less list PATCH is judged by the list member', () => {
9999
['inlineEdit', { inlineEdit: true }],
100100
['columnState', { columnState: { widths: { name: 120 } } }],
101101
['rowHeight', { rowHeight: 'compact' }],
102-
])('the console %s toggle (patch only, ruled on #7494) is ACCEPTED on listOverlay', (_label, patch) => {
102+
])('the console %s toggle (a patch-only write, as ruled) is ACCEPTED on listOverlay', (_label, patch) => {
103103
const { branch, data } = acceptance({ ...patch, viewKind: 'list', ...ID, _isOverride: true });
104104
expect(branch).toBe('listOverlay');
105105
expect(data.type).toBe('grid');
106106
});
107107
});
108108

109-
describe('#20186 …and its list keys are JUDGED, not stripped', () => {
109+
describe('…and its list keys are JUDGED, not stripped', () => {
110110
it('a retired bare-string `sort` is REFUSED at `sort`, with the 17.5.0 retirement prescription', () => {
111111
const r = refusal({ ...LIST, sort: 'name desc' });
112112
expect(r.topCodes).toEqual(['invalid_union']);
@@ -141,7 +141,7 @@ describe('#20186 …and its list keys are JUDGED, not stripped', () => {
141141
});
142142
});
143143

144-
describe('#20186 a column-less list overlay that NAMES a `type` stays refused — at `columns`', () => {
144+
describe('a column-less list overlay that NAMES a `type` stays refused — at `columns`', () => {
145145
// `overlay.list.identity` in `view-union-diagnostics.test.ts`: refused before,
146146
// refused now, and still wrapped as `invalid_union` (the refusal is an
147147
// ABORTING issue, so the list member is not the union's lone survivor).
@@ -161,7 +161,7 @@ describe('#20186 a column-less list overlay that NAMES a `type` stays refused
161161
});
162162
});
163163

164-
describe('#20186 the mirror: a `viewKind: form` body is judged by the form member', () => {
164+
describe('the mirror: a `viewKind: form` body is judged by the form member', () => {
165165
it('list `columns` on a form overlay is REFUSED under formOverlay, at `columns`, with the count prescription', () => {
166166
const r = refusal({ ...FORM, columns: ['name'] });
167167
expect(r.topCodes).toEqual(['invalid_union']);
@@ -179,7 +179,7 @@ describe('#20186 the mirror: a `viewKind: form` body is judged by the form membe
179179
});
180180
});
181181

182-
describe('#20186 W2 — a list-legal value under a key both arms declare differently is ACCEPTED', () => {
182+
describe('W2 — a list-legal value under a key both arms declare differently is ACCEPTED', () => {
183183
// Refused before: the form member judged these keys by FORM rules (a retired
184184
// `aria` tombstone, a string-only `description`, a public-link `sharing`).
185185
// Declared as a widening: `Clause-②: yes (narrowing)`.
@@ -193,7 +193,7 @@ describe('#20186 W2 — a list-legal value under a key both arms declare differe
193193
});
194194
});
195195

196-
describe('#20186 controls — byte-identical parse output (measured on origin/main ce70876e)', () => {
196+
describe('controls — byte-identical parse output (measured on origin/main ce70876e)', () => {
197197
it('a real form overlay', () => {
198198
const { data, branch } = acceptance({
199199
name: 'acct.f', object: 'account', viewKind: 'form', type: 'simple', sections: [{ label: 'Main', fields: ['name'] }],
@@ -216,7 +216,7 @@ describe('#20186 controls — byte-identical parse output (measured on origin/ma
216216
});
217217
});
218218

219-
describe('#20186 each member judges ONE viewKind — read off the members themselves', () => {
219+
describe('each member judges ONE viewKind — read off the members themselves', () => {
220220
it('a direct parse of the form member names where a list body is judged', () => {
221221
const r = VIEW_METADATA_MEMBERS.formOverlay.safeParse({ ...LIST, sort: [{ field: 'name', order: 'asc' }] });
222222
expect(r.success).toBe(false);
@@ -232,7 +232,7 @@ describe('#20186 each member judges ONE viewKind — read off the members themse
232232
expect(issue?.message).toMatch(/^This is the flattened LIST overlay member, which judges `viewKind: "list"` only\./);
233233
});
234234

235-
it('an ABSENT viewKind keeps the binding prescription (#7741), on both members', () => {
235+
it('an ABSENT viewKind keeps the binding prescription, on both members', () => {
236236
for (const member of [VIEW_METADATA_MEMBERS.listOverlay, VIEW_METADATA_MEMBERS.formOverlay]) {
237237
const r = member.safeParse({ object: 'crm_lead', hidden: true });
238238
const issue = r.error!.issues.find((i) => i.path[0] === 'viewKind');
@@ -241,7 +241,7 @@ describe('#20186 each member judges ONE viewKind — read off the members themse
241241
});
242242
});
243243

244-
describe('#20186 the premise: no pipe, and the served JSON Schema moves only by the contract', () => {
244+
describe('the premise: no pipe, and the served JSON Schema moves only by the contract', () => {
245245
it('both overlay members are still plain object schemas', () => {
246246
// A pipe serves its INPUT side to `/api/v1/meta/types/view`, and for a
247247
// transform that is `{}` (the `assertViewIdentity` docblock). The input-side

‎packages/spec/src/ui/view-overlay-viewkind-type.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ type IsOptionalKey<T, K extends keyof T> = {} extends Pick<T, K> ? true : false;
109109
const typeIsOptionalOnListOverlayOutput: IsOptionalKey<ListOverlayOut, 'type'> = true;
110110
void [typeIsOptionalOnListOverlayOutput];
111111

112-
describe('[#19920] the flattened overlay members keep their own viewKind literal', () => {
112+
describe('the flattened overlay members keep their own viewKind literal', () => {
113113
it('the list-shaped `viewKind: "form"` body is refused by every door that judges it', () => {
114114
const body = { type: 'grid', columns: ['name'], object: 'crm_lead', viewKind: 'form' };
115115
expect(VIEW_METADATA_MEMBERS.listOverlay.safeParse(body).success).toBe(false);

0 commit comments

Comments
 (0)