diff --git a/.changeset/12971-objectstack-pin-main.md b/.changeset/12971-objectstack-pin-main.md new file mode 100644 index 0000000000..5a6b580865 --- /dev/null +++ b/.changeset/12971-objectstack-pin-main.md @@ -0,0 +1,30 @@ +--- +"@object-ui/types": minor +"@object-ui/components": minor +"@object-ui/app-shell": minor +"@object-ui/console": minor +"@object-ui/runner": minor +--- + +Build `@objectstack/*` from objectstack `main` `f4755a68a7` (objectui#12971). + +`.objectstack-sha` moves from `2c9bf9430a`, the commit `@objectstack/spec` `18.0.0-next.3` was released from, to `f4755a68a7`, a commit on objectstack `main` after the `18.0.0-next.5` release, so its packages read `18.0.0-next.5`. The console and the runner bundle `@objectstack/*` built from it. No package's `@objectstack/*` range changes. + +Clause-②: yes (narrowing) + +The pin moves `@object-ui/types`' accept sets both ways: keys the spec added arrive by reference and widen them, and checks the spec added narrow them. Both are listed below. + +**Accept sets that narrow with it.** `@object-ui/types` validates a page through the spec's own page checks, so `safeValidateSchema`, `StrictAnyComponentSchema` and `objectui validate` now refuse: + +- A region named `main` with `width: 'small'`, `'medium'` or `'large'`, on a `page` node and on a page-kind node (`type: 'record'`, `'home'` or `'utility'`). FROM accepted on both faces TO refused at the region's `width`, with the spec's message (`checkPageMainRegionWidth`), which opens: "Region `main` is refused with `width: 'large'` (since @objectstack/spec 18): a `main` region takes only `width: 'full'`." No layout reads those three values on `main`; the template decides its width. Delete `width` to keep the page as it renders, or write `width: 'full'` for a full-bleed page. Other regions keep all four values. +- A `record` binding on a page-kind node. FROM kept unvalidated by `safeValidateSchema` and refused as an undeclared key by `StrictAnyComponentSchema` TO refused on both at `record`, with the spec's message (`checkPageRecordBindingPlacement`), for example on a `home` page: "`record` binds the row of an `app` page, and this page is `type: 'home'`, which has no row to bind. Delete `record`, or make the page `type: 'app'`." The check reads the page kind from `type`, so it does not judge a `page` node, whose kind is `pageType`. + +**A published input that leaves.** `action:button` and `action:icon` no longer publish a `patch` input. FROM published TO absent: the spec retired a page action row's own `patch`. A row's `operation: 'update'` runs the declared action its `name` names, and the platform action route writes that action's `patch`. The `operation` input's description now says so. + +**Accept sets that widen with it.** Three keys the spec added are taken by reference, so `StrictAnyComponentSchema` and `objectui validate` now accept them where they refused them as undeclared. Their renderer halves are separate work: + +- `emptyState` on `record:details`, `record:highlights` and `record:quick_actions`. +- `record` on a `page` node. +- `dataSource` on a navigation entry, every member but a separator. `NavigationItemSchema` and the `NavigationItem` type take the spec's `ElementApiDataSourceSchema` by reference; before, `NavigationItemSchema` stripped the key. The spec describes it as planned, and the console does not read it yet. + +**A palette exclusion that goes.** `record:json` is retired in the spec and is no longer a page component type, so the page designer's palette drops its exclusion entry. Nothing the palette offers changes. diff --git a/.objectstack-pin/client/package.json b/.objectstack-pin/client/package.json index 1891aedc34..6f55f8e787 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.3", + "version": "18.0.0-next.5", "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.3", - "@objectstack/spec": "18.0.0-next.3" + "@objectstack/core": "18.0.0-next.5", + "@objectstack/spec": "18.0.0-next.5" }, "devDependencies": { "@hono/node-server": "^2.1.1", - "@objectstack/driver-sqlite-wasm": "18.0.0-next.3", - "@objectstack/hono": "18.0.0-next.3", - "@objectstack/metadata-core": "18.0.0-next.3", - "@objectstack/metadata-protocol": "18.0.0-next.3", - "@objectstack/objectql": "18.0.0-next.3", - "@objectstack/platform-objects": "18.0.0-next.3", - "@objectstack/plugin-auth": "18.0.0-next.3", - "@objectstack/plugin-hono-server": "18.0.0-next.3", - "@objectstack/rest": "18.0.0-next.3", - "@objectstack/runtime": "18.0.0-next.3", - "@objectstack/service-analytics": "18.0.0-next.3", - "@objectstack/service-automation": "18.0.0-next.3", + "@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", "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 c86e1eb84e..7923a176b9 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.3", + "version": "18.0.0-next.5", "license": "Apache-2.0", "description": "Microkernel Core for ObjectStack", "type": "module", @@ -19,7 +19,7 @@ } }, "devDependencies": { - "@objectstack/metadata-core": "18.0.0-next.3", + "@objectstack/metadata-core": "18.0.0-next.5", "@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.3", - "@objectstack/types": "18.0.0-next.3", + "@objectstack/spec": "18.0.0-next.5", + "@objectstack/types": "18.0.0-next.5", "zod": "^4.6.5" }, "keywords": [ diff --git a/.objectstack-pin/formula/package.json b/.objectstack-pin/formula/package.json index e942c3fabf..1c88694b93 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.3", + "version": "18.0.0-next.5", "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.3" + "@objectstack/spec": "18.0.0-next.5" }, "devDependencies": { "@types/node": "^26.6.3", diff --git a/.objectstack-pin/lint/package.json b/.objectstack-pin/lint/package.json index 7494aeaca9..0460a6e763 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.3", + "version": "18.0.0-next.5", "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.3", - "@objectstack/sdui-parser": "18.0.0-next.3", - "@objectstack/spec": "18.0.0-next.3", + "@objectstack/formula": "18.0.0-next.5", + "@objectstack/sdui-parser": "18.0.0-next.5", + "@objectstack/spec": "18.0.0-next.5", "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 dd6a1c5aa4..8a9958c0ba 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.3", + "version": "18.0.0-next.5", "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 0d72da08ca..2c0ab2c59d 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.3", + "version": "18.0.0-next.5", "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 1ecc7e72ed..541ffa61ac 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.3", + "version": "18.0.0-next.5", "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.3" + "@objectstack/spec": "18.0.0-next.5" }, "devDependencies": { "typescript": "^6.0.3", diff --git a/.objectstack-sha b/.objectstack-sha index f928c13fd3..ff6357d136 100644 --- a/.objectstack-sha +++ b/.objectstack-sha @@ -1 +1 @@ -2c9bf9430a1198bfaa5c0de9ff4a811838c6869c +f4755a68a797d5a000cb8bd1c942cb2a9862108e 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 f0d0920965..0d1f8cd966 100644 --- a/apps/console/src/__tests__/registry-inputs-spec-parity.test.ts +++ b/apps/console/src/__tests__/registry-inputs-spec-parity.test.ts @@ -662,6 +662,22 @@ const MINOR_17_6_BLOCKS = ['record:line_items']; /** Does the spec this tree resolves carry the 17.6.0 set? `every`, for the reason `specCarriesGaBlocks` gives. */ const specCarries176Blocks = MINOR_17_6_BLOCKS.every((type) => type in ComponentPropsMap); +/** + * The one block the objectstack `main` pin after 18.0.0-next.3 newly carries + * (objectui#12971): the map went from 60 entries to 61 when `list-view` gained + * its row (objectstack#22444, landed as `194a42d1f7`), the list view's own + * schema. `@object-ui/plugin-list` registers it eagerly with `inputs`, so this + * file JUDGES it from that pin on. + * + * Same shape as `MINOR_17_6_BLOCKS`: this repo registered the block with + * `inputs` before the spec described it, so what moved at the bump is the + * SPEC's side. It enters `covered` the moment the installed spec carries it. + */ +const MINOR_18_MAIN_BLOCKS = ['list-view']; + +/** Does the spec this tree resolves carry the post-next.3 set? `every`, for the reason `specCarriesGaBlocks` gives. */ +const specCarries18MainBlocks = MINOR_18_MAIN_BLOCKS.every((type) => type in ComponentPropsMap); + const EXPECTED_COVERED = [ ...PINNED_EXPECTED_COVERED, ...LAZY_REGISTERED_BLOCKS, @@ -670,6 +686,7 @@ const EXPECTED_COVERED = [ ...(specCarries175Blocks ? MINOR_17_5_BLOCKS : []), ...(specCarries175LoadedBlocks ? MINOR_17_5_LOADED_BLOCKS : []), ...(specCarries176Blocks ? MINOR_17_6_BLOCKS : []), + ...(specCarries18MainBlocks ? MINOR_18_MAIN_BLOCKS : []), ].sort(); /** @@ -767,6 +784,17 @@ function isRetiredUpstream(type: string): boolean { * so the card owns no entry now and the four GA blocks carry no OWED entry * again (see THE FOUR GA BLOCKS below). * + * A sixth owner, under the objectstack `main` pin bump (objectui#12971, past + * `@objectstack/spec` 18.0.0-next.3): the seat decision D1 = A on that card + * (record 6267645900) applies decision 3 = B to the bump's one group with no + * owner, `list-view`, which that pin gives a `ComponentPropsMap` row + * (objectstack#22444), so this file judges it: + * + * - objectui#13047 — every entry on `list-view`: the 38 keys its spec row + * declares that `inputs` does not publish, and the 6 member pins its + * array/object-armed inputs owe. Expires 2026-11-18, thirty days from the + * decision, or when that card lands. + * * Each owner card decides every key by its own measurement: declare what the * renderer honours, refuse or retire what it does not. ⛔ Batch-declaring an * input a renderer does not honour is the failure decision 3 = B exists to @@ -801,8 +829,9 @@ const OBJECTUI_11438_EXPIRES = '2026-11-02'; /** * The owner cards, and nothing else may own an entry: the three decision 3 = B * names, objectui#11536, which objectui#11438 ruling A′ filed for the 17.6.0 - * bump's one group with no owner, and objectui#11068, whose row ruling A″ - * booked to it. + * bump's one group with no owner, objectui#11068, whose row ruling A″ + * booked to it, and objectui#13047, which the seat decision on objectui#12971 + * filed for the objectstack `main` pin bump's `list-view` row. */ const OBJECTUI_11111_OWNERS = [ 'objectui#11168', @@ -810,12 +839,20 @@ const OBJECTUI_11111_OWNERS = [ 'objectui#8649', 'objectui#11536', 'objectui#11068', + 'objectui#13047', ] as const; type Objectui11111Owner = (typeof OBJECTUI_11111_OWNERS)[number]; /** The ruling every entry cites, whichever bump booked it. */ const DECISION_3_B = 'objectui#11111 decision 3 = B (record 5902351047)'; +/** + * The objectstack `main` pin bump's bookings' date (objectui#12971): the seat + * decision that applied decision 3 = B to it (record 6267645900, 2026-10-19), + * plus thirty days, the distance every earlier booking kept from its ruling. + */ +const OBJECTUI_12971_EXPIRES = '2026-11-18'; + /** * Where each owner's entries were booked: the bump that surfaced them, the * record that booked them, and their expiry. The 17.5.0 owners were booked by @@ -839,6 +876,11 @@ const OBJECTUI_11111_BOOKINGS: Record< bookedBy: `objectui#11438 ruling A″ (record 5968177777), which books row 1 of the 17.6.0 bump in the shape of ${DECISION_3_B}`, expires: OBJECTUI_11438_EXPIRES, }, + 'objectui#13047': { + bump: 'objectstack `main` pin (objectui#12971)', + bookedBy: `the seat decision D1 = A on objectui#12971 (record 6267645900), which applies ${DECISION_3_B} to the objectstack \`main\` pin bump`, + expires: OBJECTUI_12971_EXPIRES, + }, }; /** The security triple 17.5.0 declares on exactly the three record blocks (objectui#8649's). */ @@ -847,16 +889,36 @@ const FIELD_SECURITY_TRIPLE = ['enforceFieldSecurity', 'redactFields', 'required /** Every entry the ruling books starts with this, so the caps can count them. */ const OWED_PREFIX = 'OWED TO '; -// The two helpers that wrote a booked entry — `OWED_TO` (the reason: owner, +// The two helpers that write a booked entry: `OWED_TO` (the reason: owner, // what is owed, the booking record and the expiry) and `owedEntries` (one -// entry per key of a block) — left with the last entry they wrote: -// `action:button.undoable`, struck by objectui#11168's last slice. Every ledger -// is empty and every cap below is 0. The cap test still reads each ledger for -// the `OWED TO ` prefix, so anything booked again is counted against a cap of 0 -// and goes red until a ruling books it with an owner and an expiry. +// entry per key of a block). They left with `action:button.undoable`, struck by +// objectui#11168's last slice, and came back with the objectstack `main` pin +// bump (objectui#12971), whose `list-view` differences the seat booked to +// objectui#13047 under decision 3 = B (record 6267645900). + +/** The reason a booked entry carries: owner, what is owed, the booking record and the expiry. */ +const OWED_TO = (owner: Objectui11111Owner, what: string): string => { + const { bump, bookedBy, expires } = OBJECTUI_11111_BOOKINGS[owner]; + return ( + `${OWED_PREFIX}${owner}. ${what} Booked by ${bookedBy}: ` + + `the ${bump} bump re-pins and declares nothing; ${owner} decides it by its own measurement. ` + + `Expires ${expires}, or when ${owner} lands, whichever is first.` + ); +}; + +/** `BLOCK.KEY` entries for every listed key of one block, all with the same owner and reason. */ +const owedEntries = ( + type: string, + keys: readonly string[], + owner: Objectui11111Owner, + what: string, +): Record => + Object.fromEntries(keys.map((key) => [`${type}.${key}`, OWED_TO(owner, what)])); /** Which owner the ruling routes an entry id to — asserted against every entry's reason. */ function objectui11111OwnerOf(id: string): Objectui11111Owner { + // The seat decision on objectui#12971 (record 6267645900) routes by BLOCK: every entry on `list-view` is objectui#13047's. + if (id.slice(0, id.indexOf('.')) === 'list-view') return 'objectui#13047'; // objectui#11438 ruling A′ routes by BLOCK: every entry on `record:line_items` is objectui#11536's. if (id.slice(0, id.indexOf('.')) === 'record:line_items') return 'objectui#11536'; // Ruling A″ routes by exact ID: row 1 is this one key, and objectui#11068 owns nothing else. @@ -882,9 +944,9 @@ const owedIdsOf = (ledger: Record): string[] => const OBJECTUI_11111_LEDGER_CAPS = { unjudgedBlocks: 0, // objectui#11168 loaded and judged all four: slice 3 object-map and object-tree, slice 4 object-gantt, slice 5 object-timeline offSpecInputs: 0, // objectui#11168 slice 1 retired action:group.name - unpublishedKeys: 0, // objectui#11168: 0 (its last slice declared action:button undoable, ruling B on objectui#11754; the two `endpoint` entries had left at the 17.6.0 bump, objectui#11438, when the spec stopped declaring the key); objectui#8652: 0, objectui#8649: 0, objectui#11536: 0 and objectui#11068: 0 (each struck by its landing; objectui#11536 declared all ten record:line_items keys, objectui#11068's build published object-grid keyboardNavigation) + unpublishedKeys: 38, // objectui#13047: 38 (list-view, booked by the seat decision D1 = A on objectui#12971, record 6267645900); objectui#11168: 0 (its last slice declared action:button undoable, ruling B on objectui#11754; the two `endpoint` entries had left at the 17.6.0 bump, objectui#11438, when the spec stopped declaring the key); objectui#8652: 0, objectui#8649: 0, objectui#11536: 0 and objectui#11068: 0 (each struck by its landing; objectui#11536 declared all ten record:line_items keys, objectui#11068's build published object-grid keyboardNavigation) refusedArms: 0, // objectui#11168: slice 2 narrowed element:definition-list.columns, slice 3 object-form.layout - memberPins: 0, // objectui#11168 slice 2 pinned element:definition-list.items and element:repeater ×3; objectui#11536 pinned record:line_items columns and dataSource + memberPins: 6, // objectui#13047: 6 (list-view data, columns, filter, sort, options and dataSource, booked by the same decision); objectui#11168 slice 2 pinned element:definition-list.items and element:repeater ×3; objectui#11536 pinned record:line_items columns and dataSource } as const; /** @@ -974,7 +1036,7 @@ const UNJUDGED_SPEC_BLOCKS: Record = { 'global:notifications': 'EMPTY SPEC SHAPE. Same shape and same reasoning as `app:launcher` above: `app-shell/src/views/global-notifications-renderer.tsx` registers it propless because the spec shape is empty, and that registration is not in this file\'s import graph. objectui#8176.', 'marketplace:installed-list': - 'EMPTY SPEC SHAPE. New in `@objectstack/spec` 17.3.0, which declares it with no top-level key at all. Not registered here since objectui#12733: objectstack#23933 stopped serving the one page that rendered it, and the list that page led to is Setup → Packages filtered to Marketplace, whose rows carry its actions (`MarketplaceRowActions.installedAppsPins.test.tsx`). The empty shape is the load-bearing half either way; the spec retiring the type makes this entry RETIRED UPSTREAM. objectui#7122, objectui#12733, ledger objectui#8176.', + 'RETIRED UPSTREAM. `@objectstack/spec` retired it by name after 18.0.0-next.3 (objectstack#24255, landed as `14a8bd2693`): out of `PageComponentType`, named in `RETIRED_PAGE_COMPONENT_TYPES`, and its row `retiredComponentProps` — a `never` that refuses every props bag, `{}` included, with a message that names the Packages view that replaces it — so there is no authoring surface for either direction to judge. Before that it stood here as EMPTY SPEC SHAPE. New in `@objectstack/spec` 17.3.0, which declared it with no top-level key at all; not registered here since objectui#12733, whose Setup → Packages list filtered to Marketplace carries its actions (`MarketplaceRowActions.installedAppsPins.test.tsx`). objectui#7122, objectui#12733, objectui#12971, ledger objectui#8176.', 'mcp:connect-agent': 'EMPTY SPEC SHAPE. New in `@objectstack/spec` 17.3.0, which declares it with no top-level key at all. Registered propless by `@object-ui/app-shell` (`src/console/connect/ConnectAgentWidget.tsx`), outside this file\'s import graph; the empty shape is the load-bearing half either way. objectui#7122, ledger objectui#8176.', 'record:approval_decision': @@ -984,7 +1046,7 @@ const UNJUDGED_SPEC_BLOCKS: Record = { 'record:attachments': 'EMPTY SPEC SHAPE. New in `@objectstack/spec` 18 (objectui#12030), which declares it with no top-level key at all. Registered by `@object-ui/app-shell` (`src/views/record-attachments-renderer.tsx`), outside this file\'s import graph; the empty shape is the load-bearing half either way, exactly as for `app:launcher` above. objectui#12030, ledger objectui#8176.', 'record:json': - 'NOT REGISTERED, DELIBERATELY. New in `@objectstack/spec` 18.0.0-next.3 (objectstack#23745, landed as `97b8e7a51d`) for the approval request page\'s raw-payload panel, which objectui#12490 was to render. The maintainer then dropped that face (pointer 6232139151 on objectui#12490: no business need for an approver to read JSON), so no renderer is built, the served-page edit closed unmerged, and the type is filed for retirement upstream (objectstack#24162). Registering it forces this entry out; the spec retiring it makes this entry RETIRED UPSTREAM. objectui#12873, ledger objectui#8176.', + 'RETIRED UPSTREAM. `@objectstack/spec` retired it by name after 18.0.0-next.3 (objectstack#24162, landed as `030a7b83e8`), the only release that declared it: out of `PageComponentType`, named in `RETIRED_PAGE_COMPONENT_TYPES`, and its row `retiredComponentProps` — a `never` that refuses every props bag, `{}` included — so there is no authoring surface for either direction to judge. Before that it stood here as NOT REGISTERED, DELIBERATELY: the maintainer dropped the approval request page\'s raw-payload panel it was declared for (pointer 6232139151 on objectui#12490). objectui#12873, objectui#12971, ledger objectui#8176.', // The fourth class, and the only one that is not mechanically "unjudgeable": // OWED. The 17.5.0 bump booked four blocks here — new in that spec, registered // with `registerLazy` by the console (`register-plugins.ts`) and not loaded by @@ -1585,6 +1647,45 @@ const UNPUBLISHED_EXEMPTIONS: Record = { // (objectui#12030): every one of the six is a tombstone now, so each entry // went dangling and stale and was deleted. The repeater reads none of its // four either (objectui#12345), and nothing here publishes the keys. + + /* + * ⚠️ THE OBJECTSTACK `main` PIN BOOKINGS — the seat decision D1 = A on + * objectui#12971 (record 6267645900), which applies objectui#11111 decision + * 3 = B to the pin bump. + * + * The spec keys the `list-view` row declares from that pin on (objectstack#22444) + * that the registration's `inputs` do not publish. The bump declares nothing: + * objectui#13047 measures each key against `ListViewBlock` / `ListView`, + * declares what is honoured and leaves the rest out. Capped at + * `OBJECTUI_11111_LEDGER_CAPS.unpublishedKeys`. + */ + ...owedEntries( + 'list-view', + [ + 'name', 'label', 'description', 'searchableFields', 'filterableFields', 'userFilters', 'resizable', + 'compactToolbar', 'selection', 'navigation', 'pagination', 'kanban', 'calendar', 'gantt', 'gallery', + 'timeline', 'chart', 'map', 'tree', 'sharing', 'requiredPermissions', 'rowHeight', 'grouping', + 'rowColor', 'hiddenFields', 'fieldOrder', 'rowActions', 'bulkActions', 'bulkActionDefs', + 'conditionalFormatting', 'inlineEdit', 'exportOptions', 'userActions', 'appearance', 'addRecord', + 'showRecordCount', 'allowPrinting', 'emptyState', + ], + 'objectui#13047', + 'A SPEC KEY NOT PUBLISHED: `list-view` entered `covered` with the objectstack `main` pin and its `inputs` omit this key its spec row declares.', + ), + + // The seat decision D2 = A on objectui#12971 (record 6267645900), the + // objectui#12370 precedent (`record:related_list.parentDataSource`, owner + // objectui#12294): `emptyState` on the three bound record blocks + // (objectstack#24270) is declared by the spec and drawn by no renderer here + // yet, so publishing it would advertise configuration the platform drops. + // objectui#12932 owns the renderer and declares the input with it, striking + // these three. + 'record:details.emptyState': + 'Declared by the spec from the objectstack `main` pin (objectstack#24270): the empty state a bound record block draws when its `dataSource` answers no row. No renderer here draws it yet; objectui#12932 draws it and declares it on the registration.', + 'record:highlights.emptyState': + 'Declared by the spec from the objectstack `main` pin (objectstack#24270): the empty state a bound record block draws when its `dataSource` answers no row. No renderer here draws it yet; objectui#12932 draws it and declares it on the registration.', + 'record:quick_actions.emptyState': + 'Declared by the spec from the objectstack `main` pin (objectstack#24270): the empty state a bound record block draws when its `dataSource` answers no row. No renderer here draws it yet; objectui#12932 draws it and declares it on the registration.', }; /** @@ -2701,7 +2802,8 @@ interface MemberPin { /** * The member pins objectui#11168 slice 1 registered for the array/object-armed - * keys `action:button` and `action:icon` publish. ONE file drives both blocks + * keys `action:button` and `action:icon` publish (`patch` left with the input, + * which the spec retired on a row, objectui#12971). ONE file drives both blocks * through the same rows (`describe.each`), so each key's claim is identical on * the two and is written once here; `actionLeafMemberPins` spells it out per * block, so every id below still names its block and its key. @@ -2719,8 +2821,6 @@ const ACTION_LEAF_MEMBER_CLAIMS: Record = { 'Members are request-body fields, merged into the body the runner\'s built-in `api` executor sends, LAST: a member overrides a static `properties.params` value of the same name while the other static values survive; without the key the body is the static values alone (objectui#11168 slice 1).', bodyShape: 'The `object` arm\'s one member is `wrap`: `{ wrap: \'data\' }` nests the static values under `data` with `bodyExtra` riding flat beside it, while `flat` and an absent key send the same flat body — asserted on the body the runner\'s built-in `api` executor sends (objectui#11168 slice 1).', - patch: - 'Members are field values for `operation: update`, merged UNDER the supplied values on the way to the write route (the `script` dispatch the runner hands an update to): a member the user also supplies loses, a member only `patch` carries is written, and the same `patch` without `operation` is merged by nothing (objectui#11168 slice 1).', locations: 'Members are placement names, read at dispatch by `@object-ui/core`\'s `createServerActionHandler` (the console\'s `script` handler is built on it): a list carrying a record-scoped member (`record_header`) refuses to run with no record or selection in scope — an error toast and no request — while an object-level list (`list_toolbar`) runs (objectui#11168 slice 1).', toast: @@ -2950,11 +3050,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.3, objectui#12370), 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-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).', }, '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.3, re-measured by objectui#12030, objectui#12370, objectui#12626, objectui#12873) 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-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).', }, 'object-grid.operations': { file: 'packages/plugin-grid/src/__tests__/gridOperationsMembers-8071.test.tsx', @@ -3030,7 +3130,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.3, re-measured by objectui#12030, objectui#12370, objectui#12626, objectui#12873) 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-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).', }, 'object-kanban.data': { file: 'packages/plugin-kanban/src/__tests__/ObjectKanban.structuredMembersReachTheirSinks-8313.test.tsx', @@ -3583,6 +3683,19 @@ const MEMBER_PIN_EXEMPTIONS: Record = { // both, and the two array keys it declared (`filter`, `sort`) with them, // each registered in `MEMBER_PINS`, so no booking is left. Header kept as a // landmark for a future grep. + + // ⚠️ OBJECTSTACK `main` PIN BOOKINGS — the seat decision D1 = A on + // objectui#12971 (record 6267645900), which applies objectui#11111 decision + // 3 = B to the pin bump. The array/object-armed inputs of `list-view`, the + // block that pin newly judges, owed a member pin by objectui#13047. Counted + // into `MEMBER_PIN_EXEMPTION_CEILING` (0 -> 6) and capped at these six; that + // card's pins strike them. + ...owedEntries( + 'list-view', + ['data', 'columns', 'filter', 'sort', 'options', 'dataSource'], + 'objectui#13047', + 'A MEMBER PIN OWED: this `list-view` input is array/object-armed on a block the objectstack `main` pin newly judges, and no pin states its members.', + ), }; /** @@ -4416,8 +4529,19 @@ const NEWLY_JUDGED_UNPINNED_MEMBERS: string[] = []; * deleted their two booked entries. The two array keys it declared beside them * (`filter`, `sort`) entered the population already pinned, so nothing stands * in the list and the ceiling follows it back to 0. + * + * ## 0 -> 6 (objectui#12971, the objectstack `main` pin bump) — a RULING, not + * room, the rise the 17.5.0 and 17.6.0 bumps took + * + * The pin newly judges `list-view`, which brings six array/object-armed inputs + * with no pin: `data`, `columns`, `filter`, `sort`, `options` and `dataSource`. + * The seat decision D1 = A on objectui#12971 (record 6267645900) applies + * objectui#11111 decision 3 = B to the bump, so all six are booked OWED to + * objectui#13047. 0 + 6 booked = 6, and `the objectui#11111 ledger is capped + * at exactly the entries it lists` caps the six and routes each to + * objectui#13047, whose pins take this back to 0. */ -const MEMBER_PIN_EXEMPTION_CEILING = 0; +const MEMBER_PIN_EXEMPTION_CEILING = 6; /** * Every test file a member pin can live in, as LAZY `?raw` loaders. @@ -4602,8 +4726,15 @@ describe('registry `inputs` vs `@objectstack/spec` ComponentPropsMap (repo-wide) // one new block, `record:json`, which this repo deliberately does not // register (its ledger entry says why), so it is ledgered (12 -> 13). // `judged` and `registeredPropless` do not move. 40 + 7 + 13 = 60. - specCarried: 60, - judged: 40, + // + // 60 -> 61 on the objectstack `main` pin after 18.0.0-next.3 + // (objectui#12971): one new row, `list-view`, registered eagerly with + // `inputs`, so it is judged (`MINOR_18_MAIN_BLOCKS`, 40 -> 41). The same + // pin retires `record:json` and `marketplace:installed-list`, whose rows + // stay as refusal doors and whose ledger entries now read RETIRED + // UPSTREAM, so `ledgeredUnjudgeable` does not move. 41 + 7 + 13 = 61. + specCarried: 61, + judged: 41, registeredPropless: 7, ledgeredUnjudgeable: 13, }); @@ -5285,6 +5416,14 @@ describe('registry `inputs` vs `@objectstack/spec` ComponentPropsMap (repo-wide) ['element:record_picker', 'filter'], ['element:record_picker', 'sort'], ['element:record_picker', 'limit'], + // Two more, harvested at the objectstack `main` pin after 18.0.0-next.3 + // (objectui#12971): objectstack#24394 retired a page action row's own + // `patch` (the row runs the declared action its `name` names, whose + // `patch` the route writes). These two were PUBLISHED, not exempted, so + // the forward direction is what turned red, and the registry stopped + // publishing them in the same change. + ['action:button', 'patch'], + ['action:icon', 'patch'], ]; for (const [type, key] of HARVESTED) { @@ -6086,6 +6225,7 @@ describe('registry `inputs` vs `@objectstack/spec` ComponentPropsMap (repo-wide) 'objectui#8649': 0, 'objectui#11536': 0, 'objectui#11068': 0, + 'objectui#13047': 44, }); }); }); diff --git a/content/docs/guide/layout.md b/content/docs/guide/layout.md index 9df55f7676..33dfcf849e 100644 --- a/content/docs/guide/layout.md +++ b/content/docs/guide/layout.md @@ -293,6 +293,12 @@ grow: `utility` is the narrowest and suits settings and forms, `record` (the def } ``` +A page laid out in `regions` can drop the cap altogether: a `main` region declared +`width: 'full'` makes the page full-bleed. That is the only `width` a `main` region takes. +The template decides how wide `main` is (the space `sidebar` and `aside` leave, or one grid +cell on `dashboard`), so `'small'`, `'medium'` and `'large'` on `main` are refused at that +region's `width`, as `@objectstack/spec` refuses them. Other regions keep all four values. + `pageType` is more than a width. A `record` page leaves its heading to a `page:header` block, so a `title` on a page with no `pageType` draws no heading. That is one more reason the settings page above says `utility`. diff --git a/e2e/live/ci/backend.env b/e2e/live/ci/backend.env index 5b4e1ae39f..aba0e6984d 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.3 +OBJECTSTACK_VERSION=18.0.0-next.5 # 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/services/MetadataService.retiredObjectEnabled.test.ts b/packages/app-shell/src/services/MetadataService.retiredObjectEnabled.test.ts index 4a41a5c4e1..3ba79cb0ad 100644 --- a/packages/app-shell/src/services/MetadataService.retiredObjectEnabled.test.ts +++ b/packages/app-shell/src/services/MetadataService.retiredObjectEnabled.test.ts @@ -149,8 +149,12 @@ describe('objectui#6238 · the schema oracle — both tombstone keys are refused // 45 at `@objectstack/spec` 18.0.0-next.0, which adopted `attachedOnRead` // (measured against a 17.7.0 control: gained set exactly `['attachedOnRead']`, // lost set empty; objectui#12030). Re-pointed the same way. + // 46 on the objectstack `main` pin after 18.0.0-next.3, which adopted + // `defaultRowScope` (objectstack#24398; measured against the next.3 pin: the + // one top-level key `ObjectSchema` gains between the two commits, none lost; + // objectui#12971). Re-pointed the same way. const accept = new Set(Object.keys(ObjectSchema.shape as Record)); - expect(accept.size).toBe(45); + expect(accept.size).toBe(46); expect(accept.has('enable')).toBe(true); expect(accept.has('enabled')).toBe(false); expect(accept.has('_deleted')).toBe(false); diff --git a/packages/app-shell/src/services/MetadataService.specKeyObjectPayload.test.ts b/packages/app-shell/src/services/MetadataService.specKeyObjectPayload.test.ts index 76ded92d73..98a8175e34 100644 --- a/packages/app-shell/src/services/MetadataService.specKeyObjectPayload.test.ts +++ b/packages/app-shell/src/services/MetadataService.specKeyObjectPayload.test.ts @@ -163,8 +163,12 @@ describe('the instrument', () => { // 45 at `@objectstack/spec` 18.0.0-next.0, which adopted `attachedOnRead` // (measured against a 17.7.0 control: gained set exactly `['attachedOnRead']`, // lost set empty; objectui#12030). Re-pointed the same way. + // 46 on the objectstack `main` pin after 18.0.0-next.3, which adopted + // `defaultRowScope` (objectstack#24398; measured against the next.3 pin: the + // one top-level key `ObjectSchema` gains between the two commits, none lost; + // objectui#12971). Re-pointed the same way. const accept = new Set(Object.keys(ObjectSchema.shape as Record)); - expect(accept.size).toBe(45); + expect(accept.size).toBe(46); // `fieldGroups` is the only grouping key on the object, and it groups the // FIELDS INSIDE one object — it is not a category for objects themselves, // so `group` has no mapping target here. diff --git a/packages/app-shell/src/views/metadata-admin/celAuthoring.ts b/packages/app-shell/src/views/metadata-admin/celAuthoring.ts index 8aa6dd3ab1..d0b4c2851d 100644 --- a/packages/app-shell/src/views/metadata-admin/celAuthoring.ts +++ b/packages/app-shell/src/views/metadata-admin/celAuthoring.ts @@ -428,7 +428,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.3 (objectui#12030, objectui#12370, objectui#12626, objectui#12873), it mostly PRESCRIBES one: the + * 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 * `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 c82fba5ed2..a2e541c11d 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.3, objectui#12030, objectui#12370, objectui#12626, objectui#12873): `waitForInput` is typed + // on the installed `@objectstack/spec` (18.0.0-next.5, objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971): `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 959c23d86b..f87da53cae 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.3` (the installed `@objectstack/spec` line): + * `18.0.0-next.5` (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 d2588742c8..ac5cc49c21 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.3`) publishes no + * string is a literal. The installed spec (`18.0.0-next.5`) 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/inspectors/nav-target.navEntryTypes-11790.test.ts b/packages/app-shell/src/views/metadata-admin/inspectors/nav-target.navEntryTypes-11790.test.ts index eaaf8654e0..c70fa82982 100644 --- a/packages/app-shell/src/views/metadata-admin/inspectors/nav-target.navEntryTypes-11790.test.ts +++ b/packages/app-shell/src/views/metadata-admin/inspectors/nav-target.navEntryTypes-11790.test.ts @@ -98,6 +98,7 @@ const BASE_VALUES: Record = { badge: 'NEW', badgeVariant: 'secondary', visible: "'admin' in current_user.positions", + dataSource: { provider: 'api', read: { url: '/api/v1/cloud/home' }, path: 'data.environments' }, requiredPermissions: ['task.read'], requiresObject: 'acme_task', requiresService: 'ai', diff --git a/packages/app-shell/src/views/metadata-admin/inspectors/nav-target.ts b/packages/app-shell/src/views/metadata-admin/inspectors/nav-target.ts index 4474c87e95..007881baa6 100644 --- a/packages/app-shell/src/views/metadata-admin/inspectors/nav-target.ts +++ b/packages/app-shell/src/views/metadata-admin/inspectors/nav-target.ts @@ -69,6 +69,10 @@ const NAV_ENTRY_BASE_KEYS = [ 'badge', 'badgeVariant', 'visible', + // The spec's base declares it since the objectstack `main` pin after + // 18.0.0-next.3 (objectstack#24332): the API binding `visible` reads, so a + // change of type keeps it beside `visible` (objectui#12971). + 'dataSource', 'requiredPermissions', 'requiresObject', 'requiresService', diff --git a/packages/app-shell/src/views/metadata-admin/previews/block-types.ts b/packages/app-shell/src/views/metadata-admin/previews/block-types.ts index f5562efbeb..98dbe3af69 100644 --- a/packages/app-shell/src/views/metadata-admin/previews/block-types.ts +++ b/packages/app-shell/src/views/metadata-admin/previews/block-types.ts @@ -240,12 +240,9 @@ export const PALETTE_EXCLUSIONS: Record = { 'record:approvals': 'renders (registered by `@object-ui/app-shell`), not yet offered: `@objectstack/spec` 18 added the type, and offering it in the palette is a Studio decision the version bump does not make', 'record:attachments': 'renders (registered by `@object-ui/app-shell`), not yet offered: `@objectstack/spec` 18 added the type, and offering it in the palette is a Studio decision the version bump does not make', 'record:approval_decision': 'renders (registered by `@object-ui/app-shell`), not yet offered: `@objectstack/spec` 18 added the type, and offering it in the palette is a Studio decision its registration does not make', - // - // `@objectstack/spec` 18.0.0-next.3 added `record:json` (objectstack#23745) for - // the approval request page's raw-payload panel. The maintainer dropped that - // face before objectui built it (objectui#12490), so nothing here registers - // the type, and it is filed for retirement upstream (objectstack#24162). - 'record:json': 'not rendered by objectui: `@objectstack/spec` 18.0.0-next.3 added it for a raw-payload panel the maintainer dropped (objectui#12490), so nothing registers it, and it is filed for retirement upstream (objectstack#24162)', + // `record:json` stood here from 18.0.0-next.3 until the spec retired it + // (objectstack#24162): it is no longer a `PageComponentType`, so it needs no + // exclusion (objectui#12971). }; /** 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 794bf63b54..418e6b7993 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.3). + * the author has ADDED but not yet configured, on the installed spec (18.0.0-next.5). * * 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/cli/src/__tests__/validate-passing-keys-11440.test.ts b/packages/cli/src/__tests__/validate-passing-keys-11440.test.ts index 69b87d9f13..a52f151684 100644 --- a/packages/cli/src/__tests__/validate-passing-keys-11440.test.ts +++ b/packages/cli/src/__tests__/validate-passing-keys-11440.test.ts @@ -95,7 +95,7 @@ describe('objectui validate — the documents objectui#11440 arms', () => { kind: 'full', regions: [ { name: 'header', width: 'full', components: [{ type: 'page:header', properties: { title: 'Welcome to the CRM' } }] }, - { name: 'main', width: 'large', components: [{ type: 'element:text', properties: { content: 'A sample page.' } }] }, + { name: 'main', components: [{ type: 'element:text', properties: { content: 'A sample page.' } }] }, ], }); expect(text).not.toContain('Schema validation failed'); diff --git a/packages/components/src/renderers/action/__tests__/action-button-icon-inputs-11168.test.tsx b/packages/components/src/renderers/action/__tests__/action-button-icon-inputs-11168.test.tsx index 62652dabef..22c36541b6 100644 --- a/packages/components/src/renderers/action/__tests__/action-button-icon-inputs-11168.test.tsx +++ b/packages/components/src/renderers/action/__tests__/action-button-icon-inputs-11168.test.tsx @@ -79,10 +79,14 @@ import '../action-icon'; const BLOCKS = ['action:button', 'action:icon'] as const; type Block = (typeof BLOCKS)[number]; -/** The spec keys slice 1 published on both blocks. */ +/** + * The spec keys slice 1 published on both blocks. `patch` left the list when the + * spec retired a row's own `patch` (objectstack#24394) and the registry stopped + * publishing it (objectui#12971). + */ const LEAF_KEYS = [ 'visible', 'disabled', 'params', 'description', 'openIn', 'method', 'bodyExtra', 'bodyShape', - 'operation', 'patch', 'confirmText', 'successMessage', 'errorMessage', 'refreshAfter', + 'operation', 'confirmText', 'successMessage', 'errorMessage', 'refreshAfter', 'locations', 'toast', 'resultDialog', 'onSuccess', 'objectName', ]; diff --git a/packages/components/src/renderers/action/action-button.tsx b/packages/components/src/renderers/action/action-button.tsx index 70dbbd6e83..909fea8a54 100644 --- a/packages/components/src/renderers/action/action-button.tsx +++ b/packages/components/src/renderers/action/action-button.tsx @@ -581,18 +581,15 @@ ComponentRegistry.register('button', ActionButtonRenderer, { description: 'How an `api` action shapes its request body: `flat` (the default) or `{ wrap: KEY }` to nest the collected values under that key, with `bodyExtra` beside it', }, + // objectui#12971: no `patch` input. `@objectstack/spec` retired a row's own + // `patch` (objectstack#24394): the row runs the declared action its `name` + // names, and the route writes THAT action's `patch`. { name: 'operation', type: 'enum', enum: ['update'], description: - '`update` declares a single-record field write: the runner dispatches it to the platform action route with `patch` merged under the collected values', - }, - { - name: 'patch', - type: 'object', - description: - 'For `operation: update`: the field values written, merged UNDER the values the user supplies, so a collected value of the same name wins', + '`update` declares a single-record field write: the row runs the declared action its `name` names, so it needs a `name`. The runner posts the collected values to the platform action route under that name, and the route writes that action\'s own `patch`', }, { name: 'confirmText', diff --git a/packages/components/src/renderers/action/action-icon.tsx b/packages/components/src/renderers/action/action-icon.tsx index 028b63b106..82fcbf45af 100644 --- a/packages/components/src/renderers/action/action-icon.tsx +++ b/packages/components/src/renderers/action/action-icon.tsx @@ -411,18 +411,15 @@ ComponentRegistry.register('icon', ActionIconRenderer, { description: 'How an `api` action shapes its request body: `flat` (the default) or `{ wrap: KEY }` to nest the collected values under that key, with `bodyExtra` beside it', }, + // objectui#12971: no `patch` input. `@objectstack/spec` retired a row's own + // `patch` (objectstack#24394): the row runs the declared action its `name` + // names, and the route writes THAT action's `patch`. { name: 'operation', type: 'enum', enum: ['update'], description: - '`update` declares a single-record field write: the runner dispatches it to the platform action route with `patch` merged under the collected values', - }, - { - name: 'patch', - type: 'object', - description: - 'For `operation: update`: the field values written, merged UNDER the values the user supplies, so a collected value of the same name wins', + '`update` declares a single-record field write: the row runs the declared action its `name` names, so it needs a `name`. The runner posts the collected values to the platform action route under that name, and the route writes that action\'s own `patch`', }, { name: 'confirmText', diff --git a/packages/core/src/evaluator/optionRules.ts b/packages/core/src/evaluator/optionRules.ts index c5a2608865..e7c06f899d 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.3, + * contract ACCEPTS. Measured on the installed `@objectstack/spec` 18.0.0-next.5, * `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 d5409ec9d2..f0c90a8054 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.3 (objectui#12030, objectui#12370, objectui#12626, objectui#12873), three legs: `{}` and `{ titleField: 't' }` both fail + * the installed 18.0.0-next.5 (objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971), 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 02b265ef85..25f8ce1a9c 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.3: `ComponentPropsMap['object-calendar']` + * installed `@objectstack/spec` 18.0.0-next.5: `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 1ab322acd3..61799316ce 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.3 (objectui#12030, objectui#12370, objectui#12626, objectui#12873): + * so in the refusal itself. Measured on the installed 18.0.0-next.5 (objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971): * * 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 83512206a3..ce09f916cc 100644 --- a/packages/plugin-detail/src/index.tsx +++ b/packages/plugin-detail/src/index.tsx @@ -969,7 +969,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.3 — read + // recommended write. (The row DOES exist as of the installed 18.0.0-next.5 — 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", … })' }, @@ -977,7 +977,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.3 (`dist/ui/index.d.ts`, the + // INSTALLED `@objectstack/spec` 18.0.0-next.5 (`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 1e886f7662..261cec23e3 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.3): both are refused at PARSE + * against the installed `@objectstack/spec` (18.0.0-next.5): 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 acdbf0504e..c295dc0160 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.3 entry carries a + * here before it is red anywhere else. The installed 18.0.0-next.5 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 5504213ae0..2ddeeb624a 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.3 lands it as a tombstone, + * member. The installed `@objectstack/spec` 18.0.0-next.5 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 d9b6336b83..33863d6e1c 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.3 pin, fourteen of them live, the + * top-level keys on the installed 18.0.0-next.5 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-tree/src/ObjectTree.schemaTyped-8655.test.ts b/packages/plugin-tree/src/ObjectTree.schemaTyped-8655.test.ts index 1fe09889d3..c6fe50ec2f 100644 --- a/packages/plugin-tree/src/ObjectTree.schemaTyped-8655.test.ts +++ b/packages/plugin-tree/src/ObjectTree.schemaTyped-8655.test.ts @@ -402,11 +402,14 @@ describe('objectui#8655 — the spec has no element schema for this node', () => }); describe('objectui#8655 — `navigation` is objectui#8652\'s, its unlock HAS happened, and objectui#11168 mirrored it', () => { - it('the spec declares it on seven element entries since 17.5.0, this one included', () => { + it('the spec declares it on seven element entries since 17.5.0, this one included, and on the `list-view` row', () => { // Through 17.4.0: ONE entry, `object-grid`, and not this one. 17.5.0 met // objectui#8652's unlock criterion (objectui#11073); mirroring is that - // card's ruled second step. + // card's ruled second step. The spec after 18.0.0-next.3 gives `list-view` + // a `ComponentPropsMap` row (objectstack#22444), the list view's own + // schema, which carries the view-level `navigation` (objectui#12971). expect(specElementsDeclaring('navigation')).toEqual([ + 'list-view', 'object-calendar', 'object-gantt', 'object-grid', @@ -461,10 +464,13 @@ describe('objectui#8655 — `tree` was a VIEW-level block, and since 17.5.0 the expect(blockKeys).toEqual(['defaultExpandedDepth', 'fields', 'labelField', 'parentField']); }); - it('…and ONE element face declares a `tree` key since 17.5.0 — this node\'s (control: one key that IS on four)', () => { + it('…and ONE element face declares a `tree` key since 17.5.0 — this node\'s — beside the `list-view` row (control: one key that IS on four)', () => { // `[]` through 17.4.0. The premise this describe is named for expired at // the bump (objectui#11073): see the UPDATE note in this file's header. - expect(specElementsDeclaring('tree')).toEqual(['object-tree']); + // The `list-view` row the spec gained after 18.0.0-next.3 (objectstack#22444) + // is the list view's own schema, so its `tree` IS the view-level block this + // describe is named for, not a second element face (objectui#12971). + expect(specElementsDeclaring('tree')).toEqual(['list-view', 'object-tree']); // FIRING CONTROL on that zero, same instrument, same corpus. expect(specElementsDeclaring('filter').length).toBeGreaterThan(3); }); 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 646e373d99..9ae97d2345 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.3 and on objectstack `main`, bare `undefined` + * mark at the pinned 18.0.0-next.5 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 b653f23675..cda25da806 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.3 + * as the spec's retired key (the branded `[REMOVED]` mark at the pinned 18.0.0-next.5 * 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.3 and on objectstack + // the branded `[REMOVED]` mark at the pinned 18.0.0-next.5 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__/imported-defaults-8317.test.ts b/packages/types/src/__tests__/imported-defaults-8317.test.ts index 5532e121cd..91fb2eade5 100644 --- a/packages/types/src/__tests__/imported-defaults-8317.test.ts +++ b/packages/types/src/__tests__/imported-defaults-8317.test.ts @@ -147,6 +147,8 @@ import { checkPageSlotPair, checkPageRegionPlacement, checkPageDefaultPlacement, + checkPageMainRegionWidth, + checkPageRecordBindingPlacement, VIEW_METADATA_MEMBERS as SpecViewMetadataMembers, // objectui#12736 — the form option's spec shape: the form-view option. FormSelectOptionSchema as SpecFormSelectOptionSchema, @@ -722,6 +724,11 @@ describe('the import boundary strips every imported default (objectui#8317)', () // `PageKindNodeSchema`, and `isDefault` placement, on `PageKindNodeSchema` only. ['checkPageRegionPlacement', checkPageRegionPlacement], ['checkPageDefaultPlacement', checkPageDefaultPlacement], + // objectui#12971: the two page checks objectstack `main` adds to `PageSchema` after + // 18.0.0-next.3 — the `main` region's `width`, re-attached on `PageNodeSchema` and + // `PageKindNodeSchema`, and `record` binding placement, on `PageKindNodeSchema` only. + ['checkPageMainRegionWidth', checkPageMainRegionWidth], + ['checkPageRecordBindingPlacement', checkPageRecordBindingPlacement], // objectui#12670: the list view chart-binding check `@objectstack/spec` 18 carries on // `ListViewSchema`, attached on the authored page `list-view` node's bag // (`ListViewPropsBag`), whose `type` is the spec's view kind. 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 3564ea7758..f8f0ba8d1b 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 @@ -339,7 +339,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.3). ⛔ Not a copy of the + * shape, off `@objectstack/spec` as installed (18.0.0-next.5). ⛔ 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. */ diff --git a/packages/types/src/__tests__/passing-keys-arms-11440.test.ts b/packages/types/src/__tests__/passing-keys-arms-11440.test.ts index c10b25d44e..41a8fa56a6 100644 --- a/packages/types/src/__tests__/passing-keys-arms-11440.test.ts +++ b/packages/types/src/__tests__/passing-keys-arms-11440.test.ts @@ -80,7 +80,7 @@ const specPage = (kind: string) => ({ kind: 'full', regions: [ { name: 'header', width: 'full', components: [{ type: 'page:header', properties: { title: 'Welcome' } }] }, - { name: 'main', width: 'large', components: [{ type: 'element:text', properties: { content: 'Hello' } }] }, + { name: 'main', components: [{ type: 'element:text', properties: { content: 'Hello' } }] }, ], }); 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 3a1fcfebc9..9c4683fdad 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.3 and on objectstack `main`, + * pinned 18.0.0-next.5 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__/record-highlights-layout-9187.test.ts b/packages/types/src/__tests__/record-highlights-layout-9187.test.ts index ea283d5482..93bc4a1d61 100644 --- a/packages/types/src/__tests__/record-highlights-layout-9187.test.ts +++ b/packages/types/src/__tests__/record-highlights-layout-9187.test.ts @@ -142,9 +142,12 @@ describe('objectui#9187 — record:highlights `layout` against the installed spe // `@objectstack/spec` 17.5.0 added the record-block security members // (`requiredPermissions`, `redactFields`, `enforceFieldSecurity`) beside // `layout` (objectui#11073). `layout`'s closed set above did not move, so - // every pin in this file still describes the contract as it stands. + // every pin in this file still describes the contract as it stands. The + // spec after 18.0.0-next.3 added `emptyState` to the bound record blocks + // (objectstack#24270), beside `layout` again (objectui#12971). expect(Object.keys(RecordHighlightsProps.def.shape).sort()).toEqual([ 'aria', + 'emptyState', 'enforceFieldSecurity', 'fields', 'layout', 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 ef54378953..6a1d9e0f49 100644 --- a/packages/types/src/__tests__/spec-object-refinements-7715.test.ts +++ b/packages/types/src/__tests__/spec-object-refinements-7715.test.ts @@ -235,6 +235,72 @@ describe('objectui#12626 — the Page `isDefault` placement refusal reaches the }); }); +describe('objectui#12971 — the Page `main` region width refusal reaches objectui on both page nodes', () => { + const region = (name: string, width?: string) => ({ name, ...(width === undefined ? {} : { width }), components: [] }); + + it.each([ + ['`large` on `main`', { regions: [region('main', 'large')] }, 'regions.0.width'], + ['`small` on a `Main` region (names match case-insensitively)', { regions: [region('header'), region('Main', 'small')] }, 'regions.1.width'], + ['`medium` on `main` of a `dashboard` page', { template: 'dashboard', regions: [region('main', 'medium')] }, 'regions.0.width'], + ])('%s: the spec refuses it, and objectui refuses with the spec\'s own issue on both page nodes', (_label, extra, at) => { + const spec = SpecPageSchema.safeParse(specPage(extra)); + expect(spec.success).toBe(false); + const specIssue = spec.error!.issues.find((i) => i.path.join('.') === at); + expect(specIssue?.code).toBe('custom'); + + const kindNode = { ...extra, type: 'home' }; + for (const r of [PageNodeSchema.safeParse(pageNode(extra)), safeValidateSchema(pageNode(extra)), safeValidateSchema(kindNode)]) { + expect(r.success).toBe(false); + const issues = r.error!.issues.map((i) => ({ code: i.code, path: i.path.join('.'), message: i.message })); + expect(issues).toContainEqual({ code: 'custom', path: at, message: specIssue!.message }); + } + }); + + it('controls: `full` on `main`, no `width` on `main`, and `large` on another region pass on the spec and both page nodes', () => { + for (const extra of [ + { regions: [region('main', 'full')] }, + { regions: [region('main')] }, + { regions: [region('main'), region('aside', 'large')] }, + ]) { + expect(SpecPageSchema.safeParse(specPage(extra)).success).toBe(true); + expect(PageNodeSchema.safeParse(pageNode(extra)).success).toBe(true); + expect(safeValidateSchema(pageNode(extra)).success).toBe(true); + expect(safeValidateSchema({ ...extra, type: 'home' }).success).toBe(true); + } + }); +}); + +describe('objectui#12971 — the Page `record` binding placement refusal reaches the page-kind node, and only there', () => { + const record = { object: 'account' }; + + it.each([ + ['a `home` page', { type: 'home', record }], + ['a `utility` page', { type: 'utility', record }], + ['a `record` page bound to an object', { type: 'record', object: 'account', record }], + ])('%s with a `record` binding: the spec refuses at `record`, and the kind node refuses with the spec\'s own issue', (_label, extra) => { + const spec = SpecPageSchema.safeParse(specPage(extra)); + expect(spec.success).toBe(false); + const specIssue = spec.error!.issues.find((i) => i.path.join('.') === 'record'); + expect(specIssue?.code).toBe('custom'); + + const r = safeValidateSchema(extra); + expect(r.success).toBe(false); + expect(r.error!.issues.map((i) => ({ code: i.code, path: i.path.join('.'), message: i.message }))) + .toContainEqual({ code: 'custom', path: 'record', message: specIssue!.message }); + }); + + it('controls: an `app` page with a `record` binding passes the spec, a kind page without one passes the kind node, and the `page` node does not judge it', () => { + expect(SpecPageSchema.safeParse(specPage({ type: 'app', record })).success).toBe(true); + expect(safeValidateSchema({ type: 'home' }).success).toBe(true); + // The `page` node's `type` is the discriminator and the kind lives on `pageType`, + // so the check is not attached there: a `record` binding on it is not refused at `record`. + for (const extra of [{ pageType: 'app', record }, { record }]) { + const r = PageNodeSchema.safeParse(pageNode(extra)); + expect(r.success, JSON.stringify(r.error?.issues)).toBe(true); + } + }); +}); + // ── The census: every object-level check the spec runs is accounted for ───── /** @@ -279,15 +345,19 @@ 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.3 finds nothing. + // (`specChecksRemovedOnMain`), which on the installed 18.0.0-next.5 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. + // `checkPageMainRegionWidth` joined after 18.0.0-next.3 (objectstack#24326) and was attached + // by objectui#12971: it reads each region's `name` and `width`, which objectui's own region + // carries with the meaning `page.tsx` reads them by; measured in its describe block above. attached: [ 'checkPageSourceCompleteness', 'checkPageRequiresKind', 'checkPageSlotPair', 'checkPageRegionPlacement', + 'checkPageMainRegionWidth', ], // `checkPagePrintComposition` joined at 18 (the `print` page, ruling B′ on objectstack#8346). // It reads `type` and `regions`, and this node carries NEITHER by reference: both are in @@ -298,7 +368,11 @@ const SITES = [ // `type`, which on this node is the component discriminator `'page'`, so attached it would // refuse every `isDefault`; `PageKindNodeSchema`, whose `type` is the page kind, attaches it // (objectui#12626, measured in its describe block above). - notAttachable: ['checkPagePrintComposition', 'checkPageDefaultPlacement'], + // `checkPageRecordBindingPlacement` joined after 18.0.0-next.3 (objectstack#24275). It judges + // `record` by the page's `type` the same way, so attached here it would refuse every `record`, + // a `pageType: 'app'` page's included; `PageKindNodeSchema` attaches it (objectui#12971, + // measured in its describe block above). + notAttachable: ['checkPagePrintComposition', 'checkPageDefaultPlacement', 'checkPageRecordBindingPlacement'], }, { site: 'ListViewSchema (objectql.zod.ts)', @@ -376,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.3 this answers empty. This reads every + * `a35f7a6`), and on the installed 18.0.0-next.5 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/app.ts b/packages/types/src/app.ts index 16d78debdc..c55466fcca 100644 --- a/packages/types/src/app.ts +++ b/packages/types/src/app.ts @@ -290,6 +290,13 @@ export interface NavigationEntryItem { /** Required permissions to see/access this item */ requiredPermissions?: string[]; + /** + * One API data source whose bound value {@link visible} reads as `data` — + * the spec's shared nav-item base member, by reference (objectui#12971). + * The spec describes it as planned and not yet rendered. + */ + dataSource?: SpecObjectNavItem['dataSource']; + /** * Runtime capability gate — name of an object that must be registered * in the runtime's SchemaRegistry for this entry to render. Used to diff --git a/packages/types/src/field-types.ts b/packages/types/src/field-types.ts index 36beae5c44..87060efad2 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.3), `FieldSchema` declares + * Measured on the installed `@objectstack/spec` (18.0.0-next.5), `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.3: + * same ruling. Measured on the installed `@objectstack/spec` 18.0.0-next.5: * `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.3: an option carrying `description` + * installed `@objectstack/spec` 18.0.0-next.5: 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 015a74a6e0..a10fd0d9cb 100644 --- a/packages/types/src/objectql.ts +++ b/packages/types/src/objectql.ts @@ -4219,7 +4219,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.3 — the + * MEASURED on the installed artifact at `@objectstack/spec` 18.0.0-next.5 — 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 c1013f673c..2285440001 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.3, against a control — `columns: '2'` — that + * on the installed pin, 18.0.0-next.5, 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. @@ -358,7 +358,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.3, + * the WHOLE document with it. Measured on the installed pin, 18.0.0-next.5, * `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 @@ -390,7 +390,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.3, + * never accepted it: measured on the installed pin, 18.0.0-next.5, * `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/app.zod.ts b/packages/types/src/zod/app.zod.ts index 074f0be2c0..a8df21a2b8 100644 --- a/packages/types/src/zod/app.zod.ts +++ b/packages/types/src/zod/app.zod.ts @@ -20,6 +20,7 @@ import { z } from 'zod'; import { AppSchema as SpecAppSchema, AppContextSelectorSchema as SpecAppContextSelectorSchema, + ElementApiDataSourceSchema as SpecElementApiDataSourceSchema, I18nLabelSchema as SpecI18nLabelSchema, NavigationAreaSchema as SpecNavigationAreaSchema, // ⚠️ NOT a crossing, so it stays RAW — see THE IMPORT BOUNDARY below. This is @@ -253,6 +254,13 @@ const NavigationItemObject = z.object({ // Visibility & Permissions visible: z.union([z.boolean(), z.string()]).optional().describe('Visibility expression'), + // objectui#12971: the spec's shared nav-item base declares one API data source + // (objectstack#24332, `ElementApiDataSourceSchema` by reference), whose bound + // value an entry's `visible` reads as `data`. Taken by reference for the + // reason `runAction` is declared above: this schema strips unknown keys, so an + // authored binding would have validated clean and been thrown away. The spec + // describes it as planned and not yet rendered; the console does not read it. + dataSource: stripImportedDefaults(SpecElementApiDataSourceSchema).optional().describe("One API data source whose bound value the entry's `visible` reads as `data` (`@objectstack/spec` `ElementApiDataSourceSchema`). Planned in the spec; the console does not read it yet"), requiredPermissions: z.array(z.string()).optional().describe('Required permissions'), // Runtime capability gates. `NavigationRenderer` has always honoured these // and `NavigationItem` has always declared them — only this schema lagged, diff --git a/packages/types/src/zod/layout.zod.ts b/packages/types/src/zod/layout.zod.ts index 1c0186e21f..1e46e7fa45 100644 --- a/packages/types/src/zod/layout.zod.ts +++ b/packages/types/src/zod/layout.zod.ts @@ -23,6 +23,8 @@ import { PageTypeSchema as SpecPageTypeSchema, PageVariableSchema as SpecPageVariableSchema, checkPageDefaultPlacement, + checkPageMainRegionWidth, + checkPageRecordBindingPlacement, checkPageRegionPlacement, checkPageRequiresKind, checkPageSlotPair, @@ -1368,29 +1370,41 @@ export const PageNodeSchema = BaseSchema.extend(SpecPageFields.shape).extend({ // does not place: on a page that names a slotted template it is drawn // nowhere and the page shows a refusal (objectui#12476); on a page that // names no template it is still stacked under `main`. - // The spec's `PageSchema` carries six on objectstack `main` (the installed - // 18.0.0-next.2 still carries the removed one below); these four are attached as-is: the first - // three read `kind`, `source`, `requires` and `slots`, which this node carries - // by reference — none is in {@link PAGE_SPEC_EXCLUDED} nor overridden above. + // - a region named `main` (case-insensitively) with `width` `'small'`, + // `'medium'` or `'large'` is refused at `regions.N.width`, because no + // layout reads `main.width` except `'full'`, which lifts the page's + // max-width cap (`checkPageMainRegionWidth`, objectstack#24326; attached + // by objectui#12971). It reads each region's `name` and `width`, which + // this node's own {@link PageNodeRegionSchema} carries with the same + // meaning: `page.tsx` places `main` by its lowercased `name` and reads its + // `width` only for `'full'`. + // The checks listed above are attached as-is: the first three read `kind`, + // `source`, `requires` and `slots`, which this node carries by reference — + // none is in {@link PAGE_SPEC_EXCLUDED} nor overridden above. // The page-parse judge of a view-data component's retired `api` binding is no // longer among them: objectstack#23784 removed it from the spec, which now // leaves that verdict to the component-props lint rule at the save door // (`validateComponentProps`, `@objectstack/lint`). So a slotted block naming // `api` passes here, as it passes the spec's own `PageSchema`; a region // block is still refused by its arm in the component union (measured in - // `__tests__/page-component-view-data-12666.test.ts`). The fifth, `checkPagePrintComposition`, + // `__tests__/page-component-view-data-12666.test.ts`). `checkPagePrintComposition` // reads `type` and `regions` as the PAGE's own, which this node does NOT carry - // by reference, so it is not attached; the census below records why. The - // sixth, `checkPageDefaultPlacement` (18.0.0-next.2), judges `isDefault` by - // the page's `type`, and on this node `type` is the component discriminator - // (`'page'`), so attached here it would refuse every `isDefault`; it is - // attached on {@link PageKindNodeSchema} below, whose `type` IS the page kind. + // by reference, so it is not attached; the census below records why. + // `checkPageDefaultPlacement` (18.0.0-next.2) judges `isDefault` by the page's + // `type`, and on this node `type` is the component discriminator (`'page'`), + // so attached here it would refuse every `isDefault`; it is attached on + // {@link PageKindNodeSchema} below, whose `type` IS the page kind. + // `checkPageRecordBindingPlacement` (objectstack#24275) refuses the page + // `record` binding on any page whose `type` is not `app`. It reads `type` the + // same way, so attached here it would refuse every `record`, a `pageType: + // 'app'` page's included; it is attached on the kind node only (objectui#12971). // `__tests__/spec-object-refinements-7715.test.ts` re-derives the count, so a // check the spec adds to `PageSchema` later reddens there. .superRefine(checkPageSourceCompleteness) .superRefine(checkPageRequiresKind) .superRefine(checkPageSlotPair) - .superRefine(checkPageRegionPlacement); + .superRefine(checkPageRegionPlacement) + .superRefine(checkPageMainRegionWidth); /** * The spec page KINDS a node may carry in `type` (objectui#11440): `record`, @@ -1438,10 +1452,11 @@ const { type: _pageNodeTypeLiteral, ...PAGE_NODE_MEMBERS } = PageNodeSchema.shap * member is {@link PageNodeSchema}'s own, by reference, and that node already * takes the spec's `PageSchema` fields by reference through * {@link SpecPageFields}. Its four refusals (`actions`, `breadcrumbs`, - * `maxWidth`, `padding`), the `body` refusal and the spec's four attachable + * `maxWidth`, `padding`), the `body` refusal and the spec's attachable * object-level checks (`source`, `requires` by `kind`, the `details` / - * `tabs` slot pair, and region placement by `template`) come with it. The spec's - * `isDefault` placement check is attached here only: it reads the page + * `tabs` slot pair, region placement by `template`, and the `main` region's + * `width`) come with it. The spec's `isDefault` placement check and its + * `record` binding placement check are attached here only: each reads the page * kind from `type`, which this node carries and the `page` node does not. `regions` stays the node's own * {@link PageNodeRegionSchema}, so each region component is judged by this * union at every depth, as on `page`. @@ -1454,9 +1469,14 @@ export const PageKindNodeSchema: PageKindNodeSchemaType = BaseSchema.extend({ type: PAGE_KIND_NODE_TYPE.describe('The spec page kind — `record`, `home` or `utility` (`@objectstack/spec` `PageTypeSchema`)'), }).superRefine(checkPageSourceCompleteness).superRefine(checkPageRequiresKind).superRefine(checkPageSlotPair) .superRefine(checkPageRegionPlacement) + .superRefine(checkPageMainRegionWidth) // On this node `type` is the spec page kind, the field the spec's - // `checkPageDefaultPlacement` judges `isDefault` by (objectui#12626). - .superRefine(checkPageDefaultPlacement); + // `checkPageDefaultPlacement` judges `isDefault` by (objectui#12626), and + // `checkPageRecordBindingPlacement` judges `record` by (objectui#12971): none + // of the three kinds is `app`, so a `record` binding is refused here, as the + // spec refuses it. + .superRefine(checkPageDefaultPlacement) + .superRefine(checkPageRecordBindingPlacement); /** * The TYPE of {@link PageKindNodeSchema}, written out BY REFERENCE to the diff --git a/packages/types/src/zod/objectql.zod.ts b/packages/types/src/zod/objectql.zod.ts index a89fecf11b..779f804204 100644 --- a/packages/types/src/zod/objectql.zod.ts +++ b/packages/types/src/zod/objectql.zod.ts @@ -1774,7 +1774,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.3 (objectui#12030, objectui#12370, objectui#12626, objectui#12873), each of which answers + * and again on the installed 18.0.0-next.5 (objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971), each of which answers * every bullet below as 17.5.0 did: * * - `CalendarConfigSchema`'s `strictObject` options carry `surface` and @@ -1799,7 +1799,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.3 (objectui#12030, objectui#12370, objectui#12626, objectui#12873). + * (objectui#11717), and again on the installed 18.0.0-next.5 (objectui#12030, objectui#12370, objectui#12626, objectui#12873, objectui#12971). * * ⇒ 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 diff --git a/scripts/check-installed-spec-pin-claims.mjs b/scripts/check-installed-spec-pin-claims.mjs index 19c48d3e5c..b11224477c 100644 --- a/scripts/check-installed-spec-pin-claims.mjs +++ b/scripts/check-installed-spec-pin-claims.mjs @@ -942,6 +942,14 @@ export const LEDGER = [ class: "historical", why: "Anchored to a named commit: \"`5488ae7` is objectui#12030's branch commit: `main` at `cea429b` with `@objectstack/*` 18.0.0-next.0 resolved\" describes the tree the eager-closure baseline was measured on. A measurement that names its tree is a record, not a claim about today (objectui#12370).", }, + { + file: "scripts/check-lockfile-integrity.mjs", + package: "@objectstack/spec", + version: "18.0.0-next.3", + sites: 2, + class: "historical", + why: "Two lines of the gate's own `--self-test`, neither a claim about today: `@objectstack/spec@18.0.0-next.3` is a synthetic REGISTRY lockfile key the fixture sets beside the pinned build (`file:` directory) to show a registry copy is a duplication and a move onto the build is clean. The line's `pinned` is the build's name, and the fixture means a registry version that is NOT the pin, so it never restamps (objectui#12971).", + }, ]; // ── The run ──────────────────────────────────────────────────────────────────