From d51d0408ae8ce54be2387f6d965ef244cef0ae1e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 14:10:21 +0000 Subject: [PATCH 1/2] chore: bump objectstack to 06cab4849a8a chore: version packages (#24524) objectstack@06cab4849a8a616487d468a11dd370e929137990 -- the commit the @objectstack/spec@18.0.0 tag names. Pin and packed manifests written by scripts/bump-objectstack.sh; pnpm-lock.yaml is unchanged (a frozen install accepts it). Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB Co-authored-by: Claude --- .objectstack-pin/client/package.json | 30 +++++++++++------------ .objectstack-pin/core/package.json | 8 +++--- .objectstack-pin/formula/package.json | 4 +-- .objectstack-pin/lint/package.json | 8 +++--- .objectstack-pin/sdui-parser/package.json | 2 +- .objectstack-pin/spec/package.json | 2 +- .objectstack-pin/types/package.json | 4 +-- .objectstack-sha | 2 +- 8 files changed, 30 insertions(+), 30 deletions(-) diff --git a/.objectstack-pin/client/package.json b/.objectstack-pin/client/package.json index 6f55f8e787..907a1d12e3 100644 --- a/.objectstack-pin/client/package.json +++ b/.objectstack-pin/client/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/client", - "version": "18.0.0-next.5", + "version": "18.0.0", "license": "Apache-2.0", "description": "Official Client SDK for ObjectStack Protocol", "main": "dist/index.js", @@ -13,23 +13,23 @@ } }, "dependencies": { - "@objectstack/core": "18.0.0-next.5", - "@objectstack/spec": "18.0.0-next.5" + "@objectstack/core": "18.0.0", + "@objectstack/spec": "18.0.0" }, "devDependencies": { "@hono/node-server": "^2.1.1", - "@objectstack/driver-sqlite-wasm": "18.0.0-next.5", - "@objectstack/hono": "18.0.0-next.5", - "@objectstack/metadata-core": "18.0.0-next.5", - "@objectstack/metadata-protocol": "18.0.0-next.5", - "@objectstack/objectql": "18.0.0-next.5", - "@objectstack/platform-objects": "18.0.0-next.5", - "@objectstack/plugin-auth": "18.0.0-next.5", - "@objectstack/plugin-hono-server": "18.0.0-next.5", - "@objectstack/rest": "18.0.0-next.5", - "@objectstack/runtime": "18.0.0-next.5", - "@objectstack/service-analytics": "18.0.0-next.5", - "@objectstack/service-automation": "18.0.0-next.5", + "@objectstack/driver-sqlite-wasm": "18.0.0", + "@objectstack/hono": "18.0.0", + "@objectstack/metadata-core": "18.0.0", + "@objectstack/metadata-protocol": "18.0.0", + "@objectstack/objectql": "18.0.0", + "@objectstack/platform-objects": "18.0.0", + "@objectstack/plugin-auth": "18.0.0", + "@objectstack/plugin-hono-server": "18.0.0", + "@objectstack/rest": "18.0.0", + "@objectstack/runtime": "18.0.0", + "@objectstack/service-analytics": "18.0.0", + "@objectstack/service-automation": "18.0.0", "tsx": "^4.23.15", "typescript": "^6.0.3", "vitest": "^4.1.11" diff --git a/.objectstack-pin/core/package.json b/.objectstack-pin/core/package.json index 7923a176b9..16f1f68b36 100644 --- a/.objectstack-pin/core/package.json +++ b/.objectstack-pin/core/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/core", - "version": "18.0.0-next.5", + "version": "18.0.0", "license": "Apache-2.0", "description": "Microkernel Core for ObjectStack", "type": "module", @@ -19,7 +19,7 @@ } }, "devDependencies": { - "@objectstack/metadata-core": "18.0.0-next.5", + "@objectstack/metadata-core": "18.0.0", "@objectstack/refd-timer-testkit": "0.1.0", "@types/node": "^26.6.3", "esbuild": "^0.28.2", @@ -27,8 +27,8 @@ "vitest": "^4.1.11" }, "dependencies": { - "@objectstack/spec": "18.0.0-next.5", - "@objectstack/types": "18.0.0-next.5", + "@objectstack/spec": "18.0.0", + "@objectstack/types": "18.0.0", "zod": "^4.6.5" }, "keywords": [ diff --git a/.objectstack-pin/formula/package.json b/.objectstack-pin/formula/package.json index 1c88694b93..5cdf868cf2 100644 --- a/.objectstack-pin/formula/package.json +++ b/.objectstack-pin/formula/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/formula", - "version": "18.0.0-next.5", + "version": "18.0.0", "license": "Apache-2.0", "description": "ObjectStack canonical expression engine — CEL (cel-js) + ObjectStack stdlib + dialect registry", "main": "dist/index.js", @@ -14,7 +14,7 @@ }, "dependencies": { "@marcbachmann/cel-js": "^8.0.0", - "@objectstack/spec": "18.0.0-next.5" + "@objectstack/spec": "18.0.0" }, "devDependencies": { "@types/node": "^26.6.3", diff --git a/.objectstack-pin/lint/package.json b/.objectstack-pin/lint/package.json index 0460a6e763..8817e51fcc 100644 --- a/.objectstack-pin/lint/package.json +++ b/.objectstack-pin/lint/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/lint", - "version": "18.0.0-next.5", + "version": "18.0.0", "license": "Apache-2.0", "description": "Static, build-time validation for an ObjectStack metadata graph — dashboard widget bindings, CEL/predicate expressions, and more. Pure (stack) => Issue[] functions shared by the CLI's `os validate` and any other consumer (e.g. AI authoring). Depends on @objectstack/spec; never on a runtime.", "type": "module", @@ -39,9 +39,9 @@ } }, "dependencies": { - "@objectstack/formula": "18.0.0-next.5", - "@objectstack/sdui-parser": "18.0.0-next.5", - "@objectstack/spec": "18.0.0-next.5", + "@objectstack/formula": "18.0.0", + "@objectstack/sdui-parser": "18.0.0", + "@objectstack/spec": "18.0.0", "ajv": "^8.20.0", "ajv-formats": "^3.0.1", "sucrase": "^3.35.1", diff --git a/.objectstack-pin/sdui-parser/package.json b/.objectstack-pin/sdui-parser/package.json index 8a9958c0ba..8478df2cb8 100644 --- a/.objectstack-pin/sdui-parser/package.json +++ b/.objectstack-pin/sdui-parser/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/sdui-parser", - "version": "18.0.0-next.5", + "version": "18.0.0", "license": "Apache-2.0", "description": "ObjectStack constrained JSX-source → SDUI SchemaNode tree compiler (parse, never execute). Isomorphic, zero React. ADR-0080.", "main": "dist/index.js", diff --git a/.objectstack-pin/spec/package.json b/.objectstack-pin/spec/package.json index 2c0ab2c59d..4022d03c77 100644 --- a/.objectstack-pin/spec/package.json +++ b/.objectstack-pin/spec/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/spec", - "version": "18.0.0-next.5", + "version": "18.0.0", "description": "ObjectStack Protocol & Specification - TypeScript Interfaces, JSON Schemas, and Convention Configurations", "license": "Apache-2.0", "main": "dist/index.js", diff --git a/.objectstack-pin/types/package.json b/.objectstack-pin/types/package.json index 541ffa61ac..213249a055 100644 --- a/.objectstack-pin/types/package.json +++ b/.objectstack-pin/types/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/types", - "version": "18.0.0-next.5", + "version": "18.0.0", "license": "Apache-2.0", "description": "Shared interfaces describing the ObjectStack Runtime environment", "main": "dist/index.js", @@ -18,7 +18,7 @@ } }, "dependencies": { - "@objectstack/spec": "18.0.0-next.5" + "@objectstack/spec": "18.0.0" }, "devDependencies": { "typescript": "^6.0.3", diff --git a/.objectstack-sha b/.objectstack-sha index ff6357d136..3d78364e53 100644 --- a/.objectstack-sha +++ b/.objectstack-sha @@ -1 +1 @@ -f4755a68a797d5a000cb8bd1c942cb2a9862108e +06cab4849a8a616487d468a11dd370e929137990 From 586a6233b5afde60595e5fa8831790b35fe5d4ba Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 14:47:37 +0000 Subject: [PATCH 2/2] test: follow the 18.0.0 pin -- filterableFields retired upstream, installed-pin claims restamped The pin delta f4755a68..06cab4849a reddens three things here: - objectstack 4bc3becd5f (objectstack#24640) carries the spec's retirement tombstone for the list view's filterableFields, whose reader objectui#13118 already removed. The object-view census's sixth bucket empties into the protocol-retired ones, the object-view anchor re-triages the key as a tombstone declared by reference, the list-view parity exemption leaves, the list-view block pins the row's refusal, and the served list form no longer offers the key. - e2e/live/ci/backend.env pairs the live lane with @objectstack 18.0.0. - 34 installed-pin sentences re-measured against the 18.0.0 build (a probe answering identically on the f4755a68 and 06cab4849a builds) and restamped. Empty-frontmatter changeset: comment-only edits in published sources. Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB Co-authored-by: Claude --- .changeset/13261-objectstack-pin-18-0-0.md | 10 ++ .../registry-inputs-spec-parity.test.ts | 13 +- e2e/live/ci/backend.env | 2 +- .../SchemaForm.topLevelUnion-11251.test.tsx | 5 +- .../src/views/metadata-admin/celAuthoring.ts | 2 +- ...FlowNodeInspector.declaredDefault.test.tsx | 2 +- .../flow-node-config.startGates-12780.test.ts | 2 +- .../inspectors/flowSingleBrace-12329.test.tsx | 2 +- ...low-canvas-seeds.saveErrors-10948.test.tsx | 2 +- packages/core/src/evaluator/optionRules.ts | 2 +- .../plugin-calendar/src/ObjectCalendar.tsx | 2 +- .../calendarUnionReads-8651.test.tsx | 2 +- .../src/MetadataFieldsPage.tsx | 2 +- packages/plugin-detail/src/index.tsx | 4 +- .../formSectionGroupReference-7051.test.tsx | 2 +- .../masterDetailDetailsMembers-8071.test.tsx | 2 +- .../src/readme-navigation-example.test.ts | 2 +- packages/plugin-kanban/src/index.tsx | 2 +- .../ListViewBlock.specInputs-13047.test.tsx | 44 ++++-- .../chartconfig-aria-retired-4044.test.ts | 2 +- ...hboard-aria-retired-contract-twins.test.ts | 4 +- .../__tests__/object-view-spec-parity.test.ts | 11 +- .../object-view-unmirrored-keys-7779.test.ts | 143 ++++++++++++------ .../record-details-top-level-9040.test.ts | 2 +- .../spec-object-refinements-7715.test.ts | 4 +- packages/types/src/field-types.ts | 6 +- packages/types/src/objectql.ts | 2 +- packages/types/src/record-components.ts | 6 +- packages/types/src/zod/objectql.zod.ts | 4 +- 29 files changed, 187 insertions(+), 101 deletions(-) create mode 100644 .changeset/13261-objectstack-pin-18-0-0.md diff --git a/.changeset/13261-objectstack-pin-18-0-0.md b/.changeset/13261-objectstack-pin-18-0-0.md new file mode 100644 index 0000000000..ec53e0f54d --- /dev/null +++ b/.changeset/13261-objectstack-pin-18-0-0.md @@ -0,0 +1,10 @@ +--- +--- + +No package released: the `@objectstack/*` pin moves to objectstack's 18.0.0 version commit `06cab4849a`, and nothing objectui publishes changes behaviour (objectui#13261). + +`.objectstack-sha` and the packed manifests in `.objectstack-pin/` move with it; `pnpm-lock.yaml` does not (the packed manifests declare the same dependency names, and a frozen install accepts the lockfile). The publish floor `scripts/objectstack-floor.mjs` computes at this pin is 18.0.0. + +The published sources touched are doc comments only. Sentences that state which `@objectstack/spec` is installed were re-measured against the 18.0.0 build, on the same probes as at the old pin with identical answers, and restamped `18.0.0` (`check:installed-pin-claims`). `tsc` keeps some of those comments in emitted `dist/` files, which are published, but they change no export, type or behaviour, so they release nothing and ship with the next release of the group. + +The tests that moved with the pin follow the spec's retirement of the list view's `filterableFields` (objectstack#24640), whose reader objectui#13118 had already removed: the sixth census bucket of `object-view-unmirrored-keys-7779` empties into the protocol-retired ones, the `list-view` parity exemption leaves, and the live-e2e backend pin reads `18.0.0`. diff --git a/apps/console/src/__tests__/registry-inputs-spec-parity.test.ts b/apps/console/src/__tests__/registry-inputs-spec-parity.test.ts index cc083e32ab..fc5d077a0d 100644 --- a/apps/console/src/__tests__/registry-inputs-spec-parity.test.ts +++ b/apps/console/src/__tests__/registry-inputs-spec-parity.test.ts @@ -1654,14 +1654,15 @@ const UNPUBLISHED_EXEMPTIONS: Record = { * `requiredPermissions`, which `ListViewBlock` now honours on a page node * (`ListViewBlock.inertKeys-13074.test.tsx`). objectui#13089 struck * `addRecord`, whose Add button the console's page host now serves a create - * for (`PageView.listViewAddRecord-13089.test.tsx`). + * for (`PageView.listViewAddRecord-13089.test.tsx`). The pin move to + * `@objectstack/spec` 18.0.0 (objectui#13261) struck `filterableFields`: the + * row carries the spec's retirement tombstone there (objectstack#24640), so + * the key is a harvested tombstone, not an unpublished live key. */ 'list-view.name': 'Unpublished by measurement (objectui#13047): read by nothing on a page `list-view` node. The node draws, queries and hands its view the same thing with and without it — the view\'s own `name` is its identity in metadata, which an embedded list has no use for.', 'list-view.sharing': 'Unpublished by measurement (objectui#13047): `sharing.type` draws a Share button with no click handler, and the sharing config is enforced nowhere in the renderer. Publishing it would advertise a dead control (objectui#13047\'s report).', - 'list-view.filterableFields': - 'Unpublished, and RETIRED (objectui#13118): the row declares it as the legacy shorthand of `userFilters.fields`, which nothing honours, and the list read it as the Filter panel\'s field list until `@objectstack/spec` retired the key in 18.0 (objectstack#24640, ruling C: no per-view Filter-panel whitelist replaces it). Nothing reads it now. The entry dies at the pin that carries the spec\'s tombstone, when the row stops declaring the key live.', }; /** @@ -3179,11 +3180,11 @@ const MEMBER_PINS: Record = { }, 'object-grid.data': { file: 'packages/plugin-grid/src/__tests__/gridDataInputContract.test.ts', - pins: 'The `object` arm is `ViewDataSchema` discriminated on `provider`: each of the three providers parses and the retired `api` one is refused (spec 18.0.0-next.5, objectui#12370, objectui#12971), none of them is an array, the declaration is one shape across both registered tags so the alias cannot drift, and it is pinned at compile time too (objectui#5090).', + pins: 'The `object` arm is `ViewDataSchema` discriminated on `provider`: each of the three providers parses and the retired `api` one is refused (spec 18.0.0, objectui#12370, objectui#12971, objectui#13261), none of them is an array, the declaration is one shape across both registered tags so the alias cannot drift, and it is pinned at compile time too (objectui#5090).', }, 'object-grid.conditionalFormatting': { file: 'packages/plugin-grid/src/__tests__/gridRowDecorationMembers-8071.test.tsx', - pins: 'The members read inside ONE formatting rule, asserted on the `style` attribute of the `` the grid paints. THREE alternative predicate members with a PRECEDENCE between them — one rule carrying `condition`, `expression` and the native `field`/`operator`/`value` triple, each naming a DIFFERENT row, is decided by `condition`; drop it and `expression` decides; drop that and the triple does — so a renderer reading any two of them as equivalent is red in a specific direction rather than merely unpinned. FOUR style members, and the sharp one is a RENAME: `backgroundColor` and `borderColor` keep their authored names on the way to the DOM while `textColor` is read as the CSS `color`, so keeping the authored spelling hands React a key it silently drops — same rules, same background, the text colour gone with nothing thrown. `style` is pinned as the BASE the three colour members override rather than replace (a non-colour member of it survives alongside the override). Two rule-list facts complete it: FIRST-MATCH-WINS with no merge (the second matching rule\'s own `textColor` never lands) and a rule carrying NO predicate member is SKIPPED rather than read as always-true, with the rule after it still deciding. ⚠️ Since objectui#11533 the AUTHORED member is ONE dialect, `{ condition, style }`: `expression`, the native triple and the three top-level colour members are refused by name on `ObjectGridSchema` and the list view (`packages/types/src/__tests__/grid-list-view-conditional-formatting-11533.test.ts`), so the rows that write them pin STORED-rule reads, which the resolver keeps as compatibility arms; a last block pins the two facts side by side — the stored native rule refused at authoring and painted at render, with its respelling painting the same row. The registration is an `array` arm; the installed spec row (18.0.0-next.5, re-measured by objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971) takes the list view\'s own `{ condition, style }` rule and refuses the native members and any other key, so the spec fixes the AUTHORED shape and the read site is the rest of the member contract, the stored-rule compatibility arms included (objectui#8071 slice 12).', + pins: 'The members read inside ONE formatting rule, asserted on the `style` attribute of the `` the grid paints. THREE alternative predicate members with a PRECEDENCE between them — one rule carrying `condition`, `expression` and the native `field`/`operator`/`value` triple, each naming a DIFFERENT row, is decided by `condition`; drop it and `expression` decides; drop that and the triple does — so a renderer reading any two of them as equivalent is red in a specific direction rather than merely unpinned. FOUR style members, and the sharp one is a RENAME: `backgroundColor` and `borderColor` keep their authored names on the way to the DOM while `textColor` is read as the CSS `color`, so keeping the authored spelling hands React a key it silently drops — same rules, same background, the text colour gone with nothing thrown. `style` is pinned as the BASE the three colour members override rather than replace (a non-colour member of it survives alongside the override). Two rule-list facts complete it: FIRST-MATCH-WINS with no merge (the second matching rule\'s own `textColor` never lands) and a rule carrying NO predicate member is SKIPPED rather than read as always-true, with the rule after it still deciding. ⚠️ Since objectui#11533 the AUTHORED member is ONE dialect, `{ condition, style }`: `expression`, the native triple and the three top-level colour members are refused by name on `ObjectGridSchema` and the list view (`packages/types/src/__tests__/grid-list-view-conditional-formatting-11533.test.ts`), so the rows that write them pin STORED-rule reads, which the resolver keeps as compatibility arms; a last block pins the two facts side by side — the stored native rule refused at authoring and painted at render, with its respelling painting the same row. The registration is an `array` arm; the installed spec row (18.0.0, re-measured by objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971, objectui#13261) takes the list view\'s own `{ condition, style }` rule and refuses the native members and any other key, so the spec fixes the AUTHORED shape and the read site is the rest of the member contract, the stored-rule compatibility arms included (objectui#8071 slice 12).', }, 'object-grid.operations': { file: 'packages/plugin-grid/src/__tests__/gridOperationsMembers-8071.test.tsx', @@ -3259,7 +3260,7 @@ const MEMBER_PINS: Record = { }, 'object-kanban.conditionalFormatting': { file: 'packages/plugin-kanban/src/__tests__/ObjectKanban.structuredMembersReachTheirSinks-8313.test.tsx', - pins: 'Members are card STYLE RULES in ONE dialect since objectui#11522, the spec list view\'s `{ condition, style }`, and it reaches the sink: a rule colours the matching card and only it, a second rule aimed at the other card does the same, and the whole `style` map arrives, with the sibling card in the same render as the live non-matching control so a green cannot come from two unstyled cards agreeing. The retired native `{ field, operator, value }` and flat-colour dialects are refused by name on the types side (`packages/types/src/__tests__/kanban-conditional-formatting.test.ts`); these rows are their respellings, painting the same cards. ⛔ NOT an identity pin and not a wire pin: `ObjectKanban.tsx` never names this key at all (measured zero, against nine for `cardFields` in the same file) — it rides the `{ ...schema }` spread into `KanbanRenderer`, which forwards it to `KanbanImpl`\'s `getCardStyles`. That makes the pin load-bearing in a way the others are not: an edit replacing that spread with an explicit key list drops the key silently and nothing else in the repo would notice. The installed spec row (18.0.0-next.5, re-measured by objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971) takes the list view\'s own `{ condition, style }` rule, so the spec fixes the authored shape and the read site is the rest of the member contract (objectui#8313).', + pins: 'Members are card STYLE RULES in ONE dialect since objectui#11522, the spec list view\'s `{ condition, style }`, and it reaches the sink: a rule colours the matching card and only it, a second rule aimed at the other card does the same, and the whole `style` map arrives, with the sibling card in the same render as the live non-matching control so a green cannot come from two unstyled cards agreeing. The retired native `{ field, operator, value }` and flat-colour dialects are refused by name on the types side (`packages/types/src/__tests__/kanban-conditional-formatting.test.ts`); these rows are their respellings, painting the same cards. ⛔ NOT an identity pin and not a wire pin: `ObjectKanban.tsx` never names this key at all (measured zero, against nine for `cardFields` in the same file) — it rides the `{ ...schema }` spread into `KanbanRenderer`, which forwards it to `KanbanImpl`\'s `getCardStyles`. That makes the pin load-bearing in a way the others are not: an edit replacing that spread with an explicit key list drops the key silently and nothing else in the repo would notice. The installed spec row (18.0.0, re-measured by objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971, objectui#13261) takes the list view\'s own `{ condition, style }` rule, so the spec fixes the authored shape and the read site is the rest of the member contract (objectui#8313).', }, 'object-kanban.data': { file: 'packages/plugin-kanban/src/__tests__/ObjectKanban.structuredMembersReachTheirSinks-8313.test.tsx', diff --git a/e2e/live/ci/backend.env b/e2e/live/ci/backend.env index aba0e6984d..ae042c211b 100644 --- a/e2e/live/ci/backend.env +++ b/e2e/live/ci/backend.env @@ -14,7 +14,7 @@ # # This file declares exactly ONE @objectstack pin, and that is the whole # design: the commit is derived from it, see below. -OBJECTSTACK_VERSION=18.0.0-next.5 +OBJECTSTACK_VERSION=18.0.0 # There is deliberately no OBJECTSTACK_REF key here. The objectstack-ai/objectstack # commit the showcase app metadata is sparse-checked-out from is DERIVED at diff --git a/packages/app-shell/src/views/metadata-admin/SchemaForm.topLevelUnion-11251.test.tsx b/packages/app-shell/src/views/metadata-admin/SchemaForm.topLevelUnion-11251.test.tsx index 11a216c9fb..c4a7c0a545 100644 --- a/packages/app-shell/src/views/metadata-admin/SchemaForm.topLevelUnion-11251.test.tsx +++ b/packages/app-shell/src/views/metadata-admin/SchemaForm.topLevelUnion-11251.test.tsx @@ -222,9 +222,12 @@ describe('control: a served schema with top-level properties renders as before ( ); expect(inputValue(`${SCOPE}name`)).toBe('crm_lead.compact'); expect(inputValue(`${SCOPE}label`)).toBe('Compact'); - for (const field of ['type', 'filter', 'searchableFields', 'filterableFields']) { + for (const field of ['type', 'filter', 'searchableFields']) { expect(byId(`${SCOPE}${field}`), field).not.toBeNull(); } + // `@objectstack/spec` 18.0.0 retired `filterableFields` (objectstack#24640) + // and the served `viewForm` stopped offering it (objectui#13261). + expect(byId(`${SCOPE}filterableFields`)).toBeNull(); expect(screen.getByRole('button', { name: /^#1 — name$/ })).toBeInTheDocument(); expect(screen.getByRole('button', { name: /^#2 — status$/ })).toBeInTheDocument(); }); diff --git a/packages/app-shell/src/views/metadata-admin/celAuthoring.ts b/packages/app-shell/src/views/metadata-admin/celAuthoring.ts index f9df94a897..597fb368d2 100644 --- a/packages/app-shell/src/views/metadata-admin/celAuthoring.ts +++ b/packages/app-shell/src/views/metadata-admin/celAuthoring.ts @@ -519,7 +519,7 @@ function loadRowCanon(): Promise { * not re-derived — and the engine's own message ships with it. * * ⚠️ The reason is NOT that the engine's message refuses a `record.` - * rewrite. Measured against the installed 18.0.0-next.5 (objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971), it mostly PRESCRIBES one: the + * rewrite. Measured against the installed 18.0.0 (objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971, objectui#13261), it mostly PRESCRIBES one: the * `current_user` text CONTAINS "To gate on record state, rewrite the predicate * against `record`." — one of the three remedies it offers, and NOT where it * ends (measured: `includes` true, `endsWith` false; every covered slot's text diff --git a/packages/app-shell/src/views/metadata-admin/inspectors/FlowNodeInspector.declaredDefault.test.tsx b/packages/app-shell/src/views/metadata-admin/inspectors/FlowNodeInspector.declaredDefault.test.tsx index a2e541c11d..7e52b30300 100644 --- a/packages/app-shell/src/views/metadata-admin/inspectors/FlowNodeInspector.declaredDefault.test.tsx +++ b/packages/app-shell/src/views/metadata-admin/inspectors/FlowNodeInspector.declaredDefault.test.tsx @@ -381,7 +381,7 @@ describe('boolean: a declared defaultValue seeds the control (objectui#8451, arm // ⇒ `screen.waitForInput` is the offline undeclared boolean that sentence // says does not exist — and it is undeclared CORRECTLY, so ⛔ do not read // this as an objectui#9277-class omission waiting to be declared. Measured - // on the installed `@objectstack/spec` (18.0.0-next.5, objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971): `waitForInput` is typed + // on the installed `@objectstack/spec` (18.0.0, objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971, objectui#13261): `waitForInput` is typed // `z.boolean().optional()` with no `.default(...)`, so an omitted key // materialises nothing and there is no spec answer for a declaration to // mirror. Declaring one here would invent a default the runtime does not diff --git a/packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.startGates-12780.test.ts b/packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.startGates-12780.test.ts index f87da53cae..fc51982612 100644 --- a/packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.startGates-12780.test.ts +++ b/packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.startGates-12780.test.ts @@ -6,7 +6,7 @@ * * The table below is what `@objectstack/service-automation`'s * `AutomationEngine` and the trigger packages read, at objectstack - * `18.0.0-next.5` (the installed `@objectstack/spec` line): + * `18.0.0` (the installed `@objectstack/spec` line): * * - `secret`: the API trigger arms from `config.secret` (`trigger-api`). * - `objectName`: lifted into the `record_change` binding, which the diff --git a/packages/app-shell/src/views/metadata-admin/inspectors/flowSingleBrace-12329.test.tsx b/packages/app-shell/src/views/metadata-admin/inspectors/flowSingleBrace-12329.test.tsx index ac5cc49c21..49fd886020 100644 --- a/packages/app-shell/src/views/metadata-admin/inspectors/flowSingleBrace-12329.test.tsx +++ b/packages/app-shell/src/views/metadata-admin/inspectors/flowSingleBrace-12329.test.tsx @@ -6,7 +6,7 @@ * * objectstack#19939 S7 deleted the engine's single-brace resolver: a value slot * refuses a `{…}` token, a text slot reads `{{ }}` holes, and every other flow - * string is a literal. The installed spec (`18.0.0-next.5`) publishes no + * string is a literal. The installed spec (`18.0.0`) publishes no * `xExpression: 'template'` mark. So the picker has two spellings left — the * bare reference and the `{{ }}` hole — and a literal position has no picker. * diff --git a/packages/app-shell/src/views/metadata-admin/previews/flow-canvas-seeds.saveErrors-10948.test.tsx b/packages/app-shell/src/views/metadata-admin/previews/flow-canvas-seeds.saveErrors-10948.test.tsx index 418e6b7993..8b8b10d805 100644 --- a/packages/app-shell/src/views/metadata-admin/previews/flow-canvas-seeds.saveErrors-10948.test.tsx +++ b/packages/app-shell/src/views/metadata-admin/previews/flow-canvas-seeds.saveErrors-10948.test.tsx @@ -2,7 +2,7 @@ /** * objectui#10948 — what the designer's live `FlowSchema` pass says about a node - * the author has ADDED but not yet configured, on the installed spec (18.0.0-next.5). + * the author has ADDED but not yet configured, on the installed spec (18.0.0). * * Spec 17.5.0 carries objectstack#20316 (a node config key its executor * contract requires, left out, and a decision branch with no `label`, are diff --git a/packages/core/src/evaluator/optionRules.ts b/packages/core/src/evaluator/optionRules.ts index e7c06f899d..1f0c665a7d 100644 --- a/packages/core/src/evaluator/optionRules.ts +++ b/packages/core/src/evaluator/optionRules.ts @@ -118,7 +118,7 @@ export function resolveVisibleOptions( * This is deliberately NOT the lenient renderer fallback AGENTS.md #0.1 * forbids. That rule governs metadata the contract REFUSES ("if the metadata * is off-spec, fix it at the producer"); an empty label is metadata the - * contract ACCEPTS. Measured on the installed `@objectstack/spec` 18.0.0-next.5, + * contract ACCEPTS. Measured on the installed `@objectstack/spec` 18.0.0, * `SelectOptionSchema`: * * { value: 'low', label: 'Low' } -> ACCEPT (lit control) diff --git a/packages/plugin-calendar/src/ObjectCalendar.tsx b/packages/plugin-calendar/src/ObjectCalendar.tsx index f0c90a8054..be235f85ca 100644 --- a/packages/plugin-calendar/src/ObjectCalendar.tsx +++ b/packages/plugin-calendar/src/ObjectCalendar.tsx @@ -1428,7 +1428,7 @@ export const ObjectCalendar: React.FC = ({ * * `@objectstack/spec`'s `CalendarConfigSchema` is a `strictObject` whose ONE * required key is `startDateField`; `titleField` is optional. Re-measured on - * the installed 18.0.0-next.5 (objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971), three legs: `{}` and `{ titleField: 't' }` both fail + * the installed 18.0.0 (objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971, objectui#13261), three legs: `{}` and `{ titleField: 't' }` both fail * `invalid_type` at `startDateField`, and `{ startDateField: 'd' }` parses * CLEAN. The spec's own note on that schema names THIS renderer as the * reason — `resolveTitle` above takes an explicit `titleField` when present diff --git a/packages/plugin-calendar/src/__tests__/calendarUnionReads-8651.test.tsx b/packages/plugin-calendar/src/__tests__/calendarUnionReads-8651.test.tsx index 25f8ce1a9c..6d3398d2f8 100644 --- a/packages/plugin-calendar/src/__tests__/calendarUnionReads-8651.test.tsx +++ b/packages/plugin-calendar/src/__tests__/calendarUnionReads-8651.test.tsx @@ -179,7 +179,7 @@ const LEDGERED_OTHER_CARD_READS: readonly string[] = []; * `ObjectCalendarSchema` and must stay so. * * ⚠️ The ground is NOT that the spec singles these two out. MEASURED on - * installed `@objectstack/spec` 18.0.0-next.5: `ComponentPropsMap['object-calendar']` + * installed `@objectstack/spec` 18.0.0: `ComponentPropsMap['object-calendar']` * is STRICT and declares exactly ten flat members — `calendar` `data` * `defaultView` `filter` `loading` `locale` `navigation` `objectName` `sort` * `staticData` (nine through 17.4.0; 17.5.0 added `navigation`) — diff --git a/packages/plugin-designer/src/MetadataFieldsPage.tsx b/packages/plugin-designer/src/MetadataFieldsPage.tsx index 61799316ce..98172c0ded 100644 --- a/packages/plugin-designer/src/MetadataFieldsPage.tsx +++ b/packages/plugin-designer/src/MetadataFieldsPage.tsx @@ -184,7 +184,7 @@ function isDesignerAuthorableType(raw: unknown): raw is DesignerFieldType { * ## Why reading the retired spelling is a RENAME here and not a laundering * * `@objectstack/spec` treats the two as one key under two spellings and says - * so in the refusal itself. Measured on the installed 18.0.0-next.5 (objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971): + * so in the refusal itself. Measured on the installed 18.0.0 (objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971, objectui#13261): * * FieldSchema.safeParse({ type: 'lookup', label: 'L', referenceTo: 'account' }) * => success = false diff --git a/packages/plugin-detail/src/index.tsx b/packages/plugin-detail/src/index.tsx index e6a73373f3..2e18b3d525 100644 --- a/packages/plugin-detail/src/index.tsx +++ b/packages/plugin-detail/src/index.tsx @@ -987,7 +987,7 @@ ComponentRegistry.register('alert', RecordAlertRenderer, { // through `pickLocalized`, which is exactly what these descriptions teach. // Declaring the map arm therefore adds no shape the block does not already // honour; it stops the manifest gate warning `type-mismatch` on the - // recommended write. (The row DOES exist as of the installed 18.0.0-next.5 — read + // recommended write. (The row DOES exist as of the installed 18.0.0 — read // for `visible` below, objectui#9100 — so the "no entry" reading is stale; // these two arms are unaffected either way.) { name: 'title', type: ['string', 'object'], description: 'Accepts an inline translation map ({ en, "zh-CN", … })' }, @@ -995,7 +995,7 @@ ComponentRegistry.register('alert', RecordAlertRenderer, { // objectui#9100 — the spec accepts three arms here and the renderer now // resolves all three, so a single `'string'` was the declaration-narrower- // than-the-contract family of objectui#4581, one layer up. Measured on the - // INSTALLED `@objectstack/spec` 18.0.0-next.5 (`dist/ui/index.d.ts`, the + // INSTALLED `@objectstack/spec` 18.0.0 (`dist/ui/index.d.ts`, the // `ComponentPropsMap['record:alert']` row): `visible` is // `boolean | string | { dialect: 'cel'|'cron'|'template', source?, … }`, // and `renderers/record-alert.tsx` hands whichever arrives to diff --git a/packages/plugin-form/src/__tests__/formSectionGroupReference-7051.test.tsx b/packages/plugin-form/src/__tests__/formSectionGroupReference-7051.test.tsx index 261cec23e3..e88ad7e2e1 100644 --- a/packages/plugin-form/src/__tests__/formSectionGroupReference-7051.test.tsx +++ b/packages/plugin-form/src/__tests__/formSectionGroupReference-7051.test.tsx @@ -448,7 +448,7 @@ describe('objectui#7051 — resolveSectionGroupReferences', () => { /** * ⭐ The second axis. The card asks that group-owned presentation keys beside * `group`, and `group` on a wizard step, not be SILENTLY accepted. Measured - * against the installed `@objectstack/spec` (18.0.0-next.5): both are refused at PARSE + * against the installed `@objectstack/spec` (18.0.0): both are refused at PARSE * — a stronger door than the lint that objectui#8497 concluded owns EXISTENCE * checking, and a different one. So authored metadata carrying either never * reaches this renderer at all; the renderer's own reports above exist for the diff --git a/packages/plugin-form/src/__tests__/masterDetailDetailsMembers-8071.test.tsx b/packages/plugin-form/src/__tests__/masterDetailDetailsMembers-8071.test.tsx index c295dc0160..ba69ed2a96 100644 --- a/packages/plugin-form/src/__tests__/masterDetailDetailsMembers-8071.test.tsx +++ b/packages/plugin-form/src/__tests__/masterDetailDetailsMembers-8071.test.tsx @@ -504,7 +504,7 @@ type SpecDetailEntry = NonNullable[numbe * The config IS the spec's entry minus `sortField`, by reference * (objectui#11396): a member the spec adds, drops or retypes lands on the * config with no hand edit, and a hand-written restatement that drifts is red - * here before it is red anywhere else. The installed 18.0.0-next.5 entry carries a + * here before it is red anywhere else. The installed 18.0.0 entry carries a * `retiredKey()` tombstone for `sortField` (objectstack-ai/objectstack#21589); * 17.6.0 declared it as a string, and the subtraction removes the key either * way. ⛔ No row here pins which of the two shapes the spec has; the `Spec Main diff --git a/packages/plugin-gantt/src/readme-navigation-example.test.ts b/packages/plugin-gantt/src/readme-navigation-example.test.ts index 2ddeeb624a..1a0cd10a9e 100644 --- a/packages/plugin-gantt/src/readme-navigation-example.test.ts +++ b/packages/plugin-gantt/src/readme-navigation-example.test.ts @@ -79,7 +79,7 @@ * * WARNING: `declaredMembers()` cannot derive that key's retirement. When this * was written the spec in the lockfile (17.4.0) still DECLARED `view` as a live - * member. The installed `@objectstack/spec` 18.0.0-next.5 lands it as a tombstone, + * member. The installed `@objectstack/spec` 18.0.0 lands it as a tombstone, * as 17.5.0 through 17.7.0 did (objectui#11073 measured it, objectui#11438, * objectui#11717 and objectui#12030 again: a `never` * schema, and `{ view: 'detail' }` is diff --git a/packages/plugin-kanban/src/index.tsx b/packages/plugin-kanban/src/index.tsx index 33863d6e1c..ad1079a9f5 100644 --- a/packages/plugin-kanban/src/index.tsx +++ b/packages/plugin-kanban/src/index.tsx @@ -554,7 +554,7 @@ export const ObjectKanbanRenderer: React.FC<{ schema: any; [key: string]: any }> * ## Why these keys were added * * `@objectstack/spec`'s `ComponentPropsMap['object-kanban']` carries FIFTEEN - * top-level keys on the installed 18.0.0-next.5 pin, fourteen of them live, the + * top-level keys on the installed 18.0.0 pin, fourteen of them live, the * same set as 17.5.0 through 17.7.0: 17.5.0 * added `navigation` and turned `quickAdd` into a tombstone (typed `never`, * refused by name — objectui#8285's ruled retirement, landed). This list diff --git a/packages/plugin-list/src/__tests__/ListViewBlock.specInputs-13047.test.tsx b/packages/plugin-list/src/__tests__/ListViewBlock.specInputs-13047.test.tsx index a6563e84de..7e396b62a7 100644 --- a/packages/plugin-list/src/__tests__/ListViewBlock.specInputs-13047.test.tsx +++ b/packages/plugin-list/src/__tests__/ListViewBlock.specInputs-13047.test.tsx @@ -39,9 +39,10 @@ * each held out of `inputs` with its reason in the console's repo-wide parity * gate: `name` (read by nothing here), `sharing` (draws a button that does * nothing on a page node), and `filterableFields` (read as the Filter panel's - * field list, not as the quick-filter shorthand the row declares it to be, + * field list, not as the quick-filter shorthand the row declared it to be, * until objectui#13118 removed that reader with the spec's retirement of the - * key; it is read as nothing now). `requiredPermissions` was measured + * key; it is read as nothing now, and since the pin at `@objectstack/spec` + * 18.0.0 the row refuses it by name, objectui#13261). `requiredPermissions` was measured * here too, until objectui#13074 honoured it on a page node; its pins now live * in `ListViewBlock.inertKeys-13074.test.tsx`, beside the `addRecord` routing * rows under a provider that serves `onCreate`. `addRecord` was held out too, @@ -779,18 +780,24 @@ describe('objectui#13047 — measured NOT honoured on a page `list-view` node (h expect(document.body.innerHTML).toBe(before); }); - it('`filterableFields`: the row calls it the shorthand of `userFilters.fields`, yet it draws no quick-filter bar', async () => { - const { dataSource } = mount({ filterableFields: ['stage'] }); - await loaded(dataSource); - expect(screen.queryByTestId('user-filters')).toBeNull(); - }); - - // objectui#13118 — this row read "what the list reads it as instead — the - // Filter panel's field list", and the second mount's first filter field was - // `Amount`, the key's first member. `@objectstack/spec` 18 retires the key - // (objectstack#24640, ruling C) and no per-view whitelist replaces it, so the - // list reads it as nothing: both mounts start "Add filter" on the same field. - it('`filterableFields`: RETIRED (objectui#13118) — the list no longer reads it as the Filter panel\'s field list, so "Add filter" starts where it does without the key', async () => { + // objectui#13261 — these rows mounted `filterableFields` as a key the row + // accepted and the list ignored (objectui#13047, then objectui#13118). The pin + // at `@objectstack/spec` 18.0.0 carries the spec's retirement tombstone + // (objectstack#24640, ruling C), so the row now refuses the key by name and no + // page node the save gate accepts can carry it. The renderer half stays + // measured, on a node built past the row on purpose. + it('`filterableFields`: RETIRED by the spec — the `list-view` row refuses it by name, with the spec\'s prescription', () => { + const row = (ComponentPropsMap as Record { success: boolean; error?: { issues: Array<{ path: PropertyKey[]; message: string }> } } }>)['list-view']; + // The accept leg first, on the same row: without it the refusal reads a row that refuses everything. + expect(row.safeParse(BASE).success).toBe(true); + const parsed = row.safeParse({ ...BASE, filterableFields: ['stage'] }); + expect(parsed.success).toBe(false); + const issues = parsed.error?.issues ?? []; + expect(issues.map((i) => i.path.join('.'))).toEqual(['filterableFields']); + expect(issues[0]?.message).toContain('was removed in @objectstack/spec 18'); + }); + + it('`filterableFields`: RETIRED (objectui#13118) — the list does not read it as the Filter panel\'s field list, so "Add filter" starts where it does without the key', async () => { const firstFilterField = async (): Promise => { fireEvent.click(screen.getByRole('button', { name: /^filter/i })); await screen.findByText('Filter Records'); @@ -809,7 +816,14 @@ describe('objectui#13047 — measured NOT honoured on a page `list-view` node (h expect(await firstFilterField()).toBe('Name'); cleanup(); - const { dataSource } = mount({ filterableFields: ['amount', 'owner'] }); + // Built past `node()`: the spec's row refuses this key (the row above), so + // only a node no save gate judged can still carry it to the renderer. + const dataSource = makeDataSource(ROWS); + render( + + + , + ); await loaded(dataSource); expect(await firstFilterField()).toBe('Name'); }); diff --git a/packages/types/src/__tests__/chartconfig-aria-retired-4044.test.ts b/packages/types/src/__tests__/chartconfig-aria-retired-4044.test.ts index 9ae97d2345..15b08179fb 100644 --- a/packages/types/src/__tests__/chartconfig-aria-retired-4044.test.ts +++ b/packages/types/src/__tests__/chartconfig-aria-retired-4044.test.ts @@ -30,7 +30,7 @@ * - **TypeScript.** `complex.ts` re-typed `chartConfig` as `any`, so an * authored `aria` compiled. The member now takes `aria` from the spec's own * input type, which is the spec's retired-key type (the branded `[REMOVED]` - * mark at the pinned 18.0.0-next.5 and on objectstack `main`, bare `undefined` + * mark at the pinned 18.0.0 and on objectstack `main`, bare `undefined` * through 17.5.0; both * spellings are `retired-key-type.ts`'s), so an authored value is a compile * error. diff --git a/packages/types/src/__tests__/dashboard-aria-retired-contract-twins.test.ts b/packages/types/src/__tests__/dashboard-aria-retired-contract-twins.test.ts index cda25da806..0ae5f495e0 100644 --- a/packages/types/src/__tests__/dashboard-aria-retired-contract-twins.test.ts +++ b/packages/types/src/__tests__/dashboard-aria-retired-contract-twins.test.ts @@ -29,7 +29,7 @@ * objectui#9736 closed that half. The interface now extends the spec's own * `Dashboard` input type (`Omit< Dashboard, … >` over the exclusion list its * mirror reads), so it inherits the spec's tombstone as a DECLARED member typed - * as the spec's retired key (the branded `[REMOVED]` mark at the pinned 18.0.0-next.5 + * as the spec's retired key (the branded `[REMOVED]` mark at the pinned 18.0.0 * and on objectstack `main`, bare `undefined` through 17.5.0) — `aria` is back in the declared key * set, but only as a refusal, * and an authored value is a compile error: the same verdict the Zod twin gives @@ -52,7 +52,7 @@ describe('the TS interface declares `aria` only as the spec tombstone (objectui# it('`aria` is declared, typed as the spec\'s retired key; the neighbours it stood beside still are', () => { // Type-level pins, erased at runtime. `aria` is a DECLARED key again — the // spec projection carries the tombstone — and its type admits no value: - // the branded `[REMOVED]` mark at the pinned 18.0.0-next.5 and on objectstack + // the branded `[REMOVED]` mark at the pinned 18.0.0 and on objectstack // `main`, bare `undefined` through 17.5.0 (both spellings are // `retired-key-type.ts`'s, objectui#11330). const ariaDeclared: 'aria' extends Declared ? true : false = true; diff --git a/packages/types/src/__tests__/object-view-spec-parity.test.ts b/packages/types/src/__tests__/object-view-spec-parity.test.ts index a9917ac918..066f5072d0 100644 --- a/packages/types/src/__tests__/object-view-spec-parity.test.ts +++ b/packages/types/src/__tests__/object-view-spec-parity.test.ts @@ -281,7 +281,6 @@ const SPEC_COUNTERPART: Record([ // list on this node, so the refusal is objectui-only surface, as `viewTabBar`'s is. 'body', 'children', + // RE-TRIAGED at the pin move to `@objectstack/spec` 18.0.0 (objectui#13261), + // out of SPEC_COUNTERPART, as that entry's note said it would be: the spec + // retired the list view's `filterableFields` (objectstack#24640, ruling C: no + // per-view Filter-panel whitelist replaces it), so its counterpart is a + // tombstone and there is nothing to rename onto. Both faces still DECLARE the + // key — the zod mirror and the TS face are the spec's slot by reference, which + // is now the spec's `never` — so an authored value is refused by name with the + // spec's own prescription, the footing `viewTabBar` stands on. Read by + // nothing since objectui#13118. + 'filterableFields', ]); // ── the suite ──────────────────────────────────────────────────────────────── diff --git a/packages/types/src/__tests__/object-view-unmirrored-keys-7779.test.ts b/packages/types/src/__tests__/object-view-unmirrored-keys-7779.test.ts index dce16141b9..3891effd1a 100644 --- a/packages/types/src/__tests__/object-view-unmirrored-keys-7779.test.ts +++ b/packages/types/src/__tests__/object-view-unmirrored-keys-7779.test.ts @@ -81,6 +81,11 @@ * reference carries the tombstone in at that bump) and is READ no longer * ({@link SPEC_RETIRED_UNREAD}). * + * ⭐ objectui#13261 was that bump: the pin moved to `@objectstack/spec` 18.0.0, + * which carries the tombstone. The sixth bucket emptied into the + * protocol-retired ones, and the node's mirror now refuses the key by name with + * the spec's own prescription. + * * ⚠️ BOTH FIGURES WERE RE-TAKEN at objectui#8980 and are NOT the ones this file * was written with. It measured 47 declared / 6 read / 41 unread, plus a * SEVENTH read, `data`, that was declared nowhere and reached the renderer @@ -305,9 +310,9 @@ const NAMED_VIEW_READ_UNDECLARED = [] as const; * 44 of objectui#7928 minus bucket ①'s twenty-three, which objectui#10758 made * the host delegation read off the named view, plus `requiredPermissions` * (objectui#12341), plus `filterableFields`, read no longer since objectui#13118 - * ({@link BUCKET_SPEC_RETIRED_AHEAD_OF_PIN}). What is left is bucket ② (the legacy `show*` spellings), - * bucket ③ (objectui-only members), the two members `@objectstack/spec` 17.5.0 - * retired ({@link BUCKET_PROTOCOL_RETIRED_TOMBSTONED}) and the one LIVE + * and retired by the installed protocol since objectui#13261. What is left is + * bucket ② (the legacy `show*` spellings), bucket ③ (objectui-only members), the + * three protocol-retired members ({@link BUCKET_PROTOCOL_RETIRED_TOMBSTONED}) and the one LIVE * protocol member the SERVER enforces ({@link BUCKET_SERVER_ENFORCED_UNREAD}) — * no other live protocol member is on it. * @@ -371,7 +376,7 @@ const NAMED_VIEW_ABSENT_CONTROL = 'stickyHeader'; /** * The protocol's own member set for a named list view — `ObjectListViewSchema`'s - * shape, off `@objectstack/spec` as installed (18.0.0-next.5). ⛔ Not a copy of the + * shape, off `@objectstack/spec` as installed (18.0.0). ⛔ Not a copy of the * objectui face and ⛔ not a hand list: it is read off the schema object, so a * spec bump that moves a key moves this. */ @@ -386,14 +391,16 @@ const PROTOCOL_LIST_VIEW_KEYS = [ ] as const; /** - * Seven of those 51 are RETIREMENT TOMBSTONES, refused by name with the - * protocol's own `[REMOVED]` text (five until `@objectstack/spec` 17.5.0 retired - * `pageName` and `tabs`, objectui#11073). ⚠️ They are why a raw key-count diff reads + * Eight of those 51 are RETIREMENT TOMBSTONES, refused by name with the + * protocol's own `[REMOVED]` text (seven until the pin move to + * `@objectstack/spec` 18.0.0 took the retirement of `filterableFields`, + * objectui#13261; five until `@objectstack/spec` 17.5.0 retired `pageName` and + * `tabs`, objectui#11073). ⚠️ They are why a raw key-count diff reads * objectui as "narrower" and is wrong to: not declaring a key the protocol * refuses is agreement, not a gap. Derived by the tombstone's own marker, not * listed by hand. */ -const PROTOCOL_RETIRED_KEYS = ['bordered', 'pageName', 'performance', 'responsive', 'striped', 'tabs', 'virtualScroll'] as const; +const PROTOCOL_RETIRED_KEYS = ['bordered', 'filterableFields', 'pageName', 'performance', 'responsive', 'striped', 'tabs', 'virtualScroll'] as const; /** * The FIFTH bucket — LIVE protocol members DECLARED here and read off no named view BY @@ -431,8 +438,11 @@ const BUCKET_SERVER_ENFORCED_UNREAD = ['requiredPermissions'] as const; * carry the tombstone yet, so the pin bump that takes the retirement reddens * it, and the member moves to {@link PROTOCOL_RETIRED_KEYS} and * {@link BUCKET_PROTOCOL_RETIRED_TOMBSTONED} in that same bump. + * + * EMPTY since objectui#13261: the pin move to `@objectstack/spec` 18.0.0 took + * the retirement, and `filterableFields` moved to both lists in that bump. */ -const BUCKET_SPEC_RETIRED_AHEAD_OF_PIN = ['filterableFields'] as const; +const BUCKET_SPEC_RETIRED_AHEAD_OF_PIN = [] as const; /** The marker every protocol tombstone's description opens with. */ const PROTOCOL_TOMBSTONE_MARKER = '[REMOVED]'; @@ -582,8 +592,20 @@ const NAMED_LIST_VIEW_TOMBSTONES = [ * and the seat's Q4 ruling on objectui#11073 retired them on objectui's face as * `?: never` tombstones: the one bucket of protocol keys objectui still names, * named only to refuse them. + * + * `filterableFields` joined at the pin move to `@objectstack/spec` 18.0.0 + * (objectui#13261), out of {@link BUCKET_SPEC_RETIRED_AHEAD_OF_PIN}: objectui#13118 + * had tombstoned it on objectui's face ahead of the pin, and the installed + * protocol now retires it too (objectstack#24640). */ -const BUCKET_PROTOCOL_RETIRED_TOMBSTONED = ['pageName', 'tabs'] as const; +const BUCKET_PROTOCOL_RETIRED_TOMBSTONED = ['filterableFields', 'pageName', 'tabs'] as const; + +/** The marker each protocol-retired member's `?: never` docblock carries: the card that tombstoned it here. */ +const PROTOCOL_RETIRED_TOMBSTONE_DOC: Readonly> = { + filterableFields: 'RETIRED (objectui#13118)', + pageName: 'RETIRED (objectui#11073)', + tabs: 'RETIRED (objectui#11073)', +}; /** * The one objectui-only member that WAS read off a named view, until @@ -642,6 +664,16 @@ const ACCEPTED: Record = { showViewSwitcher: true, }; +/** + * {@link ACCEPTED} without the key whose spec slot is a retirement tombstone at + * the installed pin (objectui#13261): the value the accept legs below parse. + * `filterableFields` is still MIRRORED — its slot is the spec's by reference — + * but that slot is now the spec's `never`, so its leg is the refusal one. + */ +const ACCEPTED_LIVE = Object.fromEntries( + Object.entries(ACCEPTED).filter(([key]) => key !== SPEC_RETIRED_UNREAD), +) as Record; + /* ── Type-level pins (invariant equality, house form) ─────────────────────── */ type Equal = @@ -1166,9 +1198,14 @@ describe('objectui#7779 — the three spec-modelled keys are the spec\'s own slo expect(SpecNavigationConfigSchema.safeParse({ mode: 'page', bogus: 1 }).success).toBe(false); }); - it('the spec describes `filterableFields` as the legacy shorthand — the deprecation the mirror now inherits by reference', () => { + // Inverted at objectui#13261: this row read "the spec describes + // `filterableFields` as the legacy shorthand". The pin at `@objectstack/spec` + // 18.0.0 carries the retirement, and the mirror inherits the tombstone by + // reference — the flip the by-reference slot was kept for. + it('the spec describes `filterableFields` as RETIRED — the tombstone the mirror now inherits by reference', () => { const slot = shapeMember(ObjectViewSchema, 'filterableFields') as { description?: string }; - expect(slot.description).toMatch(/legacy shorthand for userFilters\.fields/i); + expect(slot.description?.startsWith(PROTOCOL_TOMBSTONE_MARKER)).toBe(true); + expect(slot.description).not.toMatch(/legacy shorthand for userFilters\.fields/i); }); }); @@ -1190,30 +1227,44 @@ describe('objectui#7779 — `allowCreateView` / `viewActions` are the sibling `V /* ── The accept leg and the refusal leg, per key ──────────────────────────── */ describe('objectui#7779 — each mirrored key is accepted with its declared value and the value SURVIVES the parse', () => { - it('the documented node with all eight keys parses green, directly and through the union door', () => { - const doc = { ...NODE, ...ACCEPTED }; + it('the documented node with the seven live keys parses green, directly and through the union door', () => { + // Seven since objectui#13261: the eighth, `filterableFields`, is the spec's + // tombstone at the installed pin, and its leg is the refusal below. + const doc = { ...NODE, ...ACCEPTED_LIVE }; const r = ObjectViewSchema.safeParse(doc); expect(r.success, JSON.stringify(r.error?.issues)).toBe(true); const u = safeValidateSchema(doc); expect(u.success, u.success ? '' : JSON.stringify(u.error.issues)).toBe(true); }); - it.each(MIRRORED)('`%s` survives the parse with its value (spec defaults may be added, nothing is dropped)', (key) => { - const r = ObjectViewSchema.safeParse({ ...NODE, [key]: ACCEPTED[key] }); + it('`filterableFields` is REFUSED by name on both entry paths — the mirrored slot is the spec\'s retirement tombstone (objectui#13261)', () => { + const doc = { ...NODE, ...ACCEPTED_LIVE, [SPEC_RETIRED_UNREAD]: ACCEPTED[SPEC_RETIRED_UNREAD] }; + const r = ObjectViewSchema.safeParse(doc); + expect(r.success).toBe(false); + if (!r.success) { + const issues = r.error.issues as readonly Issue[]; + expect(refusedAt(issues, SPEC_RETIRED_UNREAD)).toBe(true); + expect(issues.some((i) => (i.message ?? '').includes('was removed in @objectstack/spec 18'))).toBe(true); + } + expect(safeValidateSchema(doc).success).toBe(false); + }); + + it.each(READ_MIRRORED)('`%s` survives the parse with its value (spec defaults may be added, nothing is dropped)', (key) => { + const r = ObjectViewSchema.safeParse({ ...NODE, [key]: ACCEPTED_LIVE[key] }); expect(r.success, JSON.stringify(r.error?.issues)).toBe(true); if (!r.success) return; const out = (r.data as Record)[key]; if (key === 'navigation') { // `NavigationConfigSchema` fills its defaults on parse; the authored // members are still there and unchanged. - expect(out).toMatchObject(ACCEPTED.navigation as Record); + expect(out).toMatchObject(ACCEPTED_LIVE.navigation as Record); } else { - expect(out).toEqual(ACCEPTED[key]); + expect(out).toEqual(ACCEPTED_LIVE[key]); } }); - it.each(MIRRORED)('`%s` is optional: the node without it parses green on both entry paths', (key) => { - const doc: Record = { ...NODE, ...ACCEPTED }; + it.each(READ_MIRRORED)('`%s` is optional: the node without it parses green on both entry paths', (key) => { + const doc: Record = { ...NODE, ...ACCEPTED_LIVE }; delete doc[key]; expect(ObjectViewSchema.safeParse(doc).success).toBe(true); expect(safeValidateSchema(doc).success).toBe(true); @@ -1743,11 +1794,11 @@ describe('objectui#7924 — the unread members, in the FOUR buckets the ruling w expect(keys).not.toContain(NAMED_VIEW_ABSENT_CONTROL); }); - it('seven of the 51 are RETIREMENT TOMBSTONES, refused BY NAME — so objectui not declaring them LIVE is agreement, not narrowness', () => { + it('eight of the 51 are RETIREMENT TOMBSTONES, refused BY NAME — so objectui not declaring them LIVE is agreement, not narrowness', () => { expect(protocolRetiredKeys()).toEqual([...PROTOCOL_RETIRED_KEYS]); const base = { label: 'Directory', columns: ['name', 'email'] }; // The accept leg first: without it every refusal below is a schema that - // refuses everything rather than a reading of these seven keys. + // refuses everything rather than a reading of these eight keys. expect(SpecObjectListViewSchema.safeParse(base).success).toBe(true); for (const key of PROTOCOL_RETIRED_KEYS) { const r = SpecObjectListViewSchema.safeParse({ ...base, [key]: true }); @@ -1761,9 +1812,10 @@ describe('objectui#7924 — the unread members, in the FOUR buckets the ruling w || (i.message ?? '').includes('was removed in @objectstack/spec'))).toBe(true); } } - // …and none of the seven is declared LIVE on the objectui face. Five were + // …and none of the eight is declared LIVE on the objectui face. Five were // never declared; the two objectui#8980 declared are `?: never` tombstones - // since objectui#11073, refused by name on this face as on the protocol's. + // since objectui#11073, and `filterableFields` since objectui#13118, each + // refused by name on this face as on the protocol's. const { names, tombstones } = namedListViewMembers(); const declaredLive = names.filter((n) => !tombstones.includes(n)); expect(PROTOCOL_RETIRED_KEYS.filter((k) => declaredLive.includes(k))).toEqual([]); @@ -1773,7 +1825,8 @@ describe('objectui#7924 — the unread members, in the FOUR buckets the ruling w it('objectui declares every LIVE protocol key on this surface — the narrower-than-protocol direction is EMPTY (inverted from "but the one pending its ruling" at objectui#12341)', () => { const declared = new Set(namedListViewMembers().names); const live = protocolListViewKeys().filter((k) => !protocolRetiredKeys().includes(k)); - expect(live).toHaveLength(44); + // 43 since objectui#13261 took the retirement of `filterableFields`. + expect(live).toHaveLength(43); const missing = live.filter((k) => !declared.has(k)); // ⭐ objectui#8979 measured SEVENTEEN live protocol keys missing here; the // objectui#8980 ruling declared them (PR #9534) and this is the zero that @@ -1834,7 +1887,7 @@ describe('objectui#7924 — the unread members, in the FOUR buckets the ruling w } }); - it('the 23 unread members partition SIX ways — 0 + 8 + 11 + 2 + 1 + 1 since objectui#13118 (0 + 8 + 11 + 2 + 1 before it, 0 + 8 + 11 + 2 before objectui#12341, 23 + 8 + 11 + 2 before objectui#10758) — disjoint and exhaustive', () => { + it('the 23 unread members partition SIX ways — 0 + 8 + 11 + 3 + 1 + 0 since objectui#13261 (0 + 8 + 11 + 2 + 1 + 1 since objectui#13118, 0 + 8 + 11 + 2 + 1 before it, 0 + 8 + 11 + 2 before objectui#12341, 23 + 8 + 11 + 2 before objectui#10758) — disjoint and exhaustive', () => { const unread = new Set(NAMED_LIST_VIEW_UNREAD as readonly string[]); const protocolKeys = new Set(protocolListViewKeys()); const retiredTombstoned = new Set(BUCKET_PROTOCOL_RETIRED_TOMBSTONED); @@ -1860,9 +1913,9 @@ describe('objectui#7924 — the unread members, in the FOUR buckets the ruling w expect(protocolDeclared).toHaveLength(0); expect(legacy).toHaveLength(8); expect(inventedOnly).toHaveLength(11); - expect(BUCKET_PROTOCOL_RETIRED_TOMBSTONED).toHaveLength(2); + expect(BUCKET_PROTOCOL_RETIRED_TOMBSTONED).toHaveLength(3); expect(BUCKET_SERVER_ENFORCED_UNREAD).toHaveLength(1); - expect(BUCKET_SPEC_RETIRED_AHEAD_OF_PIN).toHaveLength(1); + expect(BUCKET_SPEC_RETIRED_AHEAD_OF_PIN).toHaveLength(0); // Exhaustive and disjoint against the census's own unread set: a member that // lands in no bucket, or in two, fails here — which is the event that makes // the 2026-09-16 disposition ruling stale. @@ -1925,7 +1978,8 @@ describe('objectui#7924 — the unread members, in the FOUR buckets the ruling w expect(deriveNamedViewReads().reads).not.toContain(member); expect(NAMED_VIEW_READ_DECLARED as readonly string[]).not.toContain(member); expect(NAMED_LIST_VIEW_UNREAD as readonly string[]).toContain(member); - expect(BUCKET_SPEC_RETIRED_AHEAD_OF_PIN as readonly string[]).toContain(member); + // In the protocol-retired bucket since objectui#13261 (the sixth until then). + expect(BUCKET_PROTOCOL_RETIRED_TOMBSTONED as readonly string[]).toContain(member); }); it.each(BUCKET_LEGACY_SHOW_SPELLINGS)('BUCKET ② — `%s` is a legacy SPELLING: the protocol declares it nowhere, and the runtime already folds it onto its canonical twin', (member) => { @@ -1953,32 +2007,27 @@ describe('objectui#7924 — the unread members, in the FOUR buckets the ruling w expect(r.success).toBe(false); }); - it.each(BUCKET_PROTOCOL_RETIRED_TOMBSTONED)('RETIRED BY THE PROTOCOL — `%s` is a protocol tombstone at 17.5.0 and a `?: never` tombstone here, read nowhere (objectui#11073)', (member) => { + it.each(BUCKET_PROTOCOL_RETIRED_TOMBSTONED)('RETIRED BY THE PROTOCOL — `%s` is a protocol tombstone (spec 17.5.0 retired the objectui#11073 pair, spec 18.0.0 retired `filterableFields`) and a `?: never` tombstone here, read nowhere', (member) => { const { names, tombstones, docs } = namedListViewMembers(); expect(names).toContain(member); expect(tombstones).toContain(member); - expect(docs[member]).toContain('RETIRED (objectui#11073)'); + expect(docs[member]).toContain(PROTOCOL_RETIRED_TOMBSTONE_DOC[member]); expect(protocolRetiredKeys()).toContain(member); expect(deriveNamedViewReads().reads).not.toContain(member); expect(NAMED_LIST_VIEW_UNREAD as readonly string[]).toContain(member); }); - it.each(BUCKET_SPEC_RETIRED_AHEAD_OF_PIN)('RETIRED AHEAD OF THE PIN — `%s` is live at the installed spec, retired on objectstack `main`, a `?: never` tombstone here, and read nowhere (objectui#13118)', (member) => { - const { names, tombstones, docs } = namedListViewMembers(); - expect(names).toContain(member); - expect(tombstones).toContain(member); - expect(docs[member]).toContain('RETIRED (objectui#13118)'); - expect(protocolListViewKeys()).toContain(member); - // The self-expiry: this goes red at the pin bump that takes the spec's - // retirement, and the bump moves the member into the protocol-retired - // buckets rather than editing this row. - expect( - protocolRetiredKeys(), - `the installed spec now carries \`${member}\`'s tombstone: move it to PROTOCOL_RETIRED_KEYS and ` - + 'BUCKET_PROTOCOL_RETIRED_TOMBSTONED, and empty BUCKET_SPEC_RETIRED_AHEAD_OF_PIN, in the bump that took it', - ).not.toContain(member); - expect(deriveNamedViewReads().reads).not.toContain(member); - expect(NAMED_LIST_VIEW_UNREAD as readonly string[]).toContain(member); + // The sixth bucket's self-expiry fired at objectui#13261: the row that read + // "RETIRED AHEAD OF THE PIN — live at the installed spec" went red there, as + // written, and its member moved into the protocol-retired buckets. + it('RETIRED AHEAD OF THE PIN is EMPTY since objectui#13261 — the installed spec carries the tombstone objectui#13118 put here first', () => { + expect([...BUCKET_SPEC_RETIRED_AHEAD_OF_PIN]).toEqual([]); + for (const member of RETIRED_BY_13118) { + expect(protocolRetiredKeys()).toContain(member); + expect(BUCKET_PROTOCOL_RETIRED_TOMBSTONED as readonly string[]).toContain(member); + expect(namedListViewMembers().tombstones).toContain(member); + expect(deriveNamedViewReads().reads).not.toContain(member); + } }); }); diff --git a/packages/types/src/__tests__/record-details-top-level-9040.test.ts b/packages/types/src/__tests__/record-details-top-level-9040.test.ts index 9c4683fdad..13fe764d93 100644 --- a/packages/types/src/__tests__/record-details-top-level-9040.test.ts +++ b/packages/types/src/__tests__/record-details-top-level-9040.test.ts @@ -174,7 +174,7 @@ type _KeyofStillResolvesOnThisInterface = Expect< /** * The contract's face, untouched by this removal: the tombstone accepts nothing. * Its type is the spec's retired-key type — the branded `[REMOVED]` mark at the - * pinned 18.0.0-next.5 and on objectstack `main`, + * pinned 18.0.0 and on objectstack `main`, * bare `undefined` through 17.5.0 — and the helper accepts * exactly those two spellings (objectui#11330). */ diff --git a/packages/types/src/__tests__/spec-object-refinements-7715.test.ts b/packages/types/src/__tests__/spec-object-refinements-7715.test.ts index 6a1d9e0f49..0856f64dcb 100644 --- a/packages/types/src/__tests__/spec-object-refinements-7715.test.ts +++ b/packages/types/src/__tests__/spec-object-refinements-7715.test.ts @@ -345,7 +345,7 @@ const SITES = [ // objectui#12370's view-data check left this list with objectui#12666: objectstack#23784 // removed it from the spec, and 18.0.0-next.3 is the first release without it (objectstack // `a35f7a6`). The census below accounts for it by what it judges, not by name - // (`specChecksRemovedOnMain`), which on the installed 18.0.0-next.5 finds nothing. + // (`specChecksRemovedOnMain`), which on the installed 18.0.0 finds nothing. // `checkPageRegionPlacement` joined at 18.0.0-next.2 and was attached by objectui#12626: it // reads `kind` and `template` (by reference) and each region's `name`, which objectui's own // region carries with the meaning `page.tsx` places it by; measured in its describe block above. @@ -450,7 +450,7 @@ const PAGE_SITE = 'SpecPageFields → PageNodeSchema (layout.zod.ts)'; * against its own spec tree, so no file here may name that export. Through * 18.0.0-next.2 the spec exported it and attached it to `PageSchema`, where * the census would miscount by one; 18.0.0-next.3 dropped it (objectstack - * `a35f7a6`), and on the installed 18.0.0-next.5 this answers empty. This reads every + * `a35f7a6`), and on the installed 18.0.0 this answers empty. This reads every * exported check no site names and keeps the ones that refuse a slotted * `object-grid` naming `api` at its `properties.data.provider`: on next.2 that * is exactly the removed judge, and on a spec without it the list is empty, so diff --git a/packages/types/src/field-types.ts b/packages/types/src/field-types.ts index 87060efad2..931d7de333 100644 --- a/packages/types/src/field-types.ts +++ b/packages/types/src/field-types.ts @@ -25,7 +25,7 @@ import type { Field as SpecField } from '@objectstack/spec/data'; * sibling field names, or `{ field, param }` entries mapping a sibling onto the * remote query parameter a dependent lookup filters by. * - * Measured on the installed `@objectstack/spec` (18.0.0-next.5), `FieldSchema` declares + * Measured on the installed `@objectstack/spec` (18.0.0), `FieldSchema` declares * `dependsOn` as an OPTIONAL ARRAY of `string | { field, param? }` — never a * bare string. That is deliberately narrower than `DependsOnInput` (`form.ts`), * the shape the widget prop `FieldWidgetComponentProps.dependsOn` and @@ -343,7 +343,7 @@ export interface MarkdownFieldMetadata extends BaseFieldMetadata { * annotated literal rejected. Follows the `TextareaFieldMetadata` precedent. * * A DECLARED spec key as of `@objectstack/spec` 17.3.0, which implements that - * same ruling. Measured on the installed `@objectstack/spec` 18.0.0-next.5: + * same ruling. Measured on the installed `@objectstack/spec` 18.0.0: * `FieldSchema` ACCEPTS `rows` on all four of textarea/markdown/html/ * richtext, as an integer of at least 1 — a non-integer answers * `invalid_type` and 0 answers `too_small`, so "declared" does not mean "any @@ -692,7 +692,7 @@ export interface SelectOptionMetadata extends SelectOptionBase { * * A DECLARED `SelectOptionSchema` key as of `@objectstack/spec` 17.3.0, * which implements the objectui#6153 half of that ruling. Measured on the - * installed `@objectstack/spec` 18.0.0-next.5: an option carrying `description` + * installed `@objectstack/spec` 18.0.0: an option carrying `description` * is ACCEPTED, as a string (a non-string answers `invalid_type`; the empty * string is valid), and a field whose `options` carry it parses whole. ⇒ it * may now be written into authored object metadata, which is the point of diff --git a/packages/types/src/objectql.ts b/packages/types/src/objectql.ts index d0598d27eb..22fbb6e953 100644 --- a/packages/types/src/objectql.ts +++ b/packages/types/src/objectql.ts @@ -4268,7 +4268,7 @@ export interface ObjectCalendarSchema extends BaseSchema { * (the spec's own `ObjectCalendarPropsSchema`) declares * `z.array(z.unknown()).optional()`, described *"Pre-fetched records — skips * the internal fetch"*. - * MEASURED on the installed artifact at `@objectstack/spec` 18.0.0-next.5 — the + * MEASURED on the installed artifact at `@objectstack/spec` 18.0.0 — the * version this repository's `pnpm-lock.yaml` resolves — through the published * `@objectstack/spec/ui` entry point: the provider block returns * `success=false` with `expected: 'array'` at `path: ['data']`, the array diff --git a/packages/types/src/record-components.ts b/packages/types/src/record-components.ts index e9161a724d..a0dcdb2680 100644 --- a/packages/types/src/record-components.ts +++ b/packages/types/src/record-components.ts @@ -107,7 +107,7 @@ export interface RecordDetailsComponentProps { * It was `number` here until objectui#8604, which is the wrong PRIMITIVE * TYPE, not merely a wider range: `{ columns: 2 }` compiled locally and the * contract refused it at publish with `invalid_value` at `columns` (measured - * on the installed pin, 18.0.0-next.5, against a control — `columns: '2'` — that + * on the installed pin, 18.0.0, against a control — `columns: '2'` — that * parses green on the same instrument). Contract-first (Commandment #0.1): * the code moves to the contract's spelling, and today's `columns: 2` * authors are the defect surfacing rather than collateral damage. @@ -367,7 +367,7 @@ export interface RecordHighlightsComponentProps { * `RecordHighlightsProps.fields[]`'s object arm declares exactly * `name`/`label`/`type`/`readonly` and carries a `never` catchall, i.e. it is * `$strict`: an unlisted key is REFUSED, not stripped, and the refusal takes - * the WHOLE document with it. Measured on the installed pin, 18.0.0-next.5, + * the WHOLE document with it. Measured on the installed pin, 18.0.0, * `RecordHighlightsProps.safeParse({ fields: [{ name: 'x', icon: 'star' }] })` * is RED with `invalid_union` at `fields.0`. So `{ name: 'amount', icon: * 'dollar-sign' }` type-checked here and was refused at the door — a green @@ -399,7 +399,7 @@ export interface RecordHighlightsComponentProps { * `z.enum(['horizontal','vertical'])` behind a `.default('horizontal')`). * * It offered a third value, `grid`, until objectui#9187, and the contract - * never accepted it: measured on the installed pin, 18.0.0-next.5, + * never accepted it: measured on the installed pin, 18.0.0, * `RecordHighlightsProps.safeParse({ fields: ['name'], layout: 'grid' })` is * RED with `invalid_value` at `layout`. So `{ layout: 'grid' }` type-checked * here and was refused at the door — a green local build and a rejection at diff --git a/packages/types/src/zod/objectql.zod.ts b/packages/types/src/zod/objectql.zod.ts index 3ab1d41b3c..1f03721294 100644 --- a/packages/types/src/zod/objectql.zod.ts +++ b/packages/types/src/zod/objectql.zod.ts @@ -1781,7 +1781,7 @@ const KanbanConfig = stripImportedDefaults(SpecListViewSchema).shape.kanban.unwr * `@objectstack/spec` 17.4.0 (the version in the lockfile then) rather than * reading it — RE-RUN on 17.5.0 (objectui#11073), whose answer moved, as the * last bullet says, RE-RUN on 17.6.0 (objectui#11438), on 17.7.0 (objectui#11717), - * and again on the installed 18.0.0-next.5 (objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971), each of which answers + * and again on the installed 18.0.0 (objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971, objectui#13261), each of which answers * every bullet below as 17.5.0 did: * * - `CalendarConfigSchema`'s `strictObject` options carry `surface` and @@ -1806,7 +1806,7 @@ const KanbanConfig = stripImportedDefaults(SpecListViewSchema).shape.kanban.unwr * prescribes neither. `endField` still draws no hint, and a one-char typo * still resolves by distance (`titleFeld` → `titleField`) — all three * measured on 17.5.0, again on 17.6.0 (objectui#11438), on 17.7.0 - * (objectui#11717), and again on the installed 18.0.0-next.5 (objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971). + * (objectui#11717), and again on the installed 18.0.0 (objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971, objectui#13261). * * ⇒ through 17.4.0 a generic typo-distance suggester picked the wrong sibling. * It was not a declaration, it contradicted no declaration, and ⛔ no upstream