Skip to content

Commit bf492c8

Browse files
objectstack-fleet[bot]claudeos-sales
authored
chore(objectui): bump the console pin to f0268ad (carries objectui#11880) (#22412)
Fixes #22385 Clause-②: no This moves the bundled Console's objectui pin from `a58626c88dc8` (PR #22015) to `f0268ad784854568aa58a2aa791f6a7502259186`, which was objectui `main` when this run started. The new pin contains `5bc55c0c5a1e`, the closing merge of objectui#11880, so it carries both objectui halves of that card: `element:record_picker` and `element:number` read their query from the node-level `dataSource` only (objectui `74add0c12`), and `element:repeater` reads that binding first (objectui `aaba8655a`). Ruling `6051498447` on #11509 orders the work this way ("objectui first …, and the spec retirement ships with the pin bump that carries it"). The spec retirement itself is #11509's, not this PR's: no key retires, no schema shape changes, no conversion or lint rule changes here. The bump follows the repo's own procedure (`scripts/bump-objectui.sh`, then `pnpm objectui:build` and `node scripts/gen-sdui-manifest-node.mjs`) and the gates it derives. It adds no step to that procedure. This is not a release act. Version Packages PR #21988 is untouched. That PR edits `packages/console/CHANGELOG.md` and `packages/console/package.json`, and this diff edits neither. The console's release input is the new `.changeset/console-f0268ad78485.md`. ## Range - objectui `git ls-remote … refs/heads/main` read `f0268ad784854568aa58a2aa791f6a7502259186` at 2026-10-09T02:23:09Z, the same sha the dispatch read at 01:49Z. - **REST `compare` on objectstack-ai/objectui proves containment** (base...head, head = the new pin, read 02:24:10Z): - `5bc55c0c5a1e...f0268ad`: `status: ahead`, `ahead_by: 8`, `behind_by: 0`, merge base `5bc55c0c5a1e4202a55a45c092cce3880a09b27a`. The pin contains objectui#11880's closing merge. - `74add0c12a70...f0268ad`: `ahead`, 27 ahead, 0 behind (objectui#11880, `element:record_picker` / `element:number`). - `aaba8655aec5...f0268ad`: `ahead`, 17 ahead, 0 behind (objectui#11880, the repeater half). - `a58626c88dc8...f0268ad`: `ahead`, 170 ahead, 0 behind. - The old pin was behind: `5bc55c0c5a1e...a58626c` reads `behind`, `behind_by: 162`. - `a58626c88dc8..f0268ad` has 170 commits, 0 merges and 1061 changed paths (482 added, 2 deleted). - `git merge-base --is-ancestor 5bc55c0 f0268ad` in the objectui object store exits 0. ## Digest level and declared-breaking entries The bump's own digest output: objectui declared 175 changesets over the range. 173 release, 2 are release-nothing, and 4 commits carry no changeset (listed by subject in the console changeset). One changeset added in the range was already consumed by an objectui release, and was read from the commit that added it. None declares `major`. Four carry the author's breaking annotation. **The highest declared level is `minor`, so the console changeset is `minor`.** Six commit subjects carry `!` (objectui#6152 rounds 7 to 12, `@object-ui/types` faces taking `@objectstack/spec` rows by reference); none of their changesets is annotated breaking, so the digest lists them at their declared level. The script wrote its `adr-0087: TODO` placeholder into the console changeset. It is answered `not-required (no-migration-prescription)`, in the wording the previous bumps used. `check-adr-0087-registration --base origin/main` reports "1 declared-breaking changeset(s), each carrying an ADR-0087 disposition". `check-changeset-no-major --base origin/main` reports "This diff introduces no `major` bump". Each declared-breaking entry, with its disposition: 1. **objectui#11070 (`18d7b489c`)**: objectui's form-field faces take `scale` and `currencyConfig` by reference to `@objectstack/spec`'s `FieldSchema`, so objectui's tolerant face now refuses what the spec already refuses. It follows this repo's existing declaration, so nothing here moves. 2. **objectui#11070 (`172acc313`)**: objectui's `sidebar` node declares `side` as `left` or `right`. `sidebar` has no `@objectstack/spec` row. 3. **objectui#11389 (`d92b2a15d`)**: the metric sub-caption is retired on the reader side. The ObjectStack half shipped in `@objectstack/spec` 17.7.0, with the conversion `translation-widget-sub-caption-removed` and the semantic entry `translation-widget-sub-caption-retired`. This repo's `sdui-parser` copy already drops `description` from `CONSUMED_WIDGET_OPTION_KEYS`, so the renderer and the save gate now agree. 4. **objectui#11880 (`74add0c12`)**: `element:record_picker` and `element:number` read their query from the node-level `dataSource` only. The flat keys they stop reading are still declared by `@objectstack/spec`. Their retirement, with its ADR-0087 conversion onto `dataSource`, is #11509, which the ruling sequences after this bump and ships in the same release. ## User-visible changes this brings into the Console The full list is the console changeset (173 entries, capped at 100 in the rendered list). The ones a Console user meets: - **Page elements and objectui#11880.** - `element:repeater` reads the node-level `dataSource` binding first, and its flat keys second. A repeater bound only through `dataSource` now renders its rows; at the old pin it rendered nothing. - `element:record_picker` and `element:number` read their object, filter, sort and limit from `dataSource` only. A page that wrote those keys flat under `properties` now shows no records (the picker) or the "No object named: set dataSource.object." notice (the number) until it moves them into `dataSource`. No example or package source in this repo writes the flat keys: the showcase's `page-variables` picker already binds through `dataSource`. - Studio's page designer writes `element:number`'s object to `dataSource.object`. - **Dashboards.** A metric tile no longer draws a sub-caption from `options.description` (objectui#11389). - **Time zones.** Dates and times render in the time zone the server answers for the workspace (objectui#11693). An administrator is asked once to set the workspace timezone while it is still the platform default (objectui#11758), and a new workspace takes its creator's browser timezone (objectui#11908). - **Lists.** - A list's Filter panel conditions, search term, sort and toolbar grouping are in the URL, so a list can be shared or bookmarked (objectui#11860). - The Filter, Sort, Group, hide-fields and Row color lists offer only fields the user may read (objectui#11925, objectui#11943, objectui#11984). - List titles use the object's plural label (objectui#11696), and empty-list copy matches the page (objectui#11687). - **Records and actions.** - A record action greyed out by its declared `disabled` predicate says why (objectui#11811, objectui#11839). - `action:button` offers Undo for an `undoable` update of the record in scope (objectui#11168). - A record delete confirmation names the record and confirms with a destructive Delete button (objectui#11695). - The record page says whether a failed read was no access, not found, or could not load (objectui#11902). - **Maps.** Without WebGL2 a map lists its records instead of crashing (objectui#11819). - **Studio.** The Interfaces pillar can create a page, a dashboard or a report in a writable package (objectui#11823). The "New" menus for automations, actions and validation rules open on common starting points (objectui#11861). Studio pickers use the shared `Select` (objectui#11865). The form designer can be arranged from the keyboard (objectui#11871, objectui#11898). The field inspector picks a field type from a searchable, grouped list (objectui#11793). - **Sign-in and settings.** The login and register pages say when the server cannot be reached (objectui#11806). Installed Apps reads a package the runtime refused to load as "Not loaded" (objectui#11645). The organization Settings page no longer offers a slug edit the framework refuses (objectui#11720). - **Accessibility.** Screen readers can name and reach the view tab bar, the settings form, the sidebar menus, the selection column and the percent cell (objectui#11690). ## What changed here (22 files, +809 / -278) | file | written by | gate that required it | |---|---|---| | `.objectui-sha` | `OBJECTUI_ROOT=… scripts/bump-objectui.sh f0268ad --no-commit` | Console Pin Gate builds what it names | | `.changeset/console-f0268ad78485.md` | the same run (the digest); the only later edit is the ADR-0087 answer above | `check-adr-0087-registration`, `check:objectui-changeset` | | `sdui.manifest.json`, `scripts/sdui-manifest.record.json` | `node scripts/gen-sdui-manifest-node.mjs` over `.cache/objectui-f0268ad78485` | `check-sdui-manifest.mjs` | | `packages/sdui-parser/objectui-lockstep.json` | `node scripts/check-sdui-lockstep.mjs --update` (`pnpm gen:sdui-lockstep`) against the same tree | `check:sdui-lockstep` | | 55 asserting citations in 15 files under `packages/spec/src` (the generated `migrations/registry.ts` among them), plus the quoted anchors of one dated `view.zod.ts` record | re-measured by hand (below); `registry.ts` by `gen:migration-registry` | `check:objectui-pin-citations` (and `--verify-anchors`) | | `.changeset/objectui-pin-citations-f0268ad78485.md` | hand-written, `@objectstack/spec` patch | Check Changeset (the shipped describes and docblocks name the pin) | | `content/docs/references/ui/view.mdx` | `check:generated --fix` | `check:docs` | - **The bump script.** It ran with `OBJECTUI_ROOT` at a detached objectui worktree of my own (`/home/user/objectui-issue-22385`, at the pin), after `git fetch origin main` there. The objectui store was shallow and did not hold the old pin, so the script ran its own `git fetch --unshallow`, then re-checked: "the range walks completely now". The shared objectui checkout's working tree, branch and index were not touched; the fetch and the deepen added objects and refs to its shared object store only. - **The manifest.** It still has 107 components, and its sha256 moves from `e0654735a318…` to `097ae3cbcf95…`. objectui's workspace version stays 17.7.0. Ten components' inputs moved: - `action:button` gains an `undoable` input. `element:repeater` gains the `dataSource` input and its `object` description says a node-level `dataSource.object` wins. - `element:number`'s `object` and `filter` descriptions now say NOT READ (objectui#11880). - `element:text`'s `variant` enum loses `heading` and `subheading`, which `@objectstack/spec` 17.7.0 already refuses. - `object-grid`, `object-kanban`, `object-calendar`, `object-gantt`, `object-master-detail-form` and `object-timeline` reword input descriptions (objectui#6152 rounds 7 to 12): the `filter` inputs name the `ViewFilterRule` array, and `resizable` says the legacy `resizableColumns` is refused. - **The lockstep.** It records 214 grammar lines (blob `0131f27cf86d`), 25 diagnostic codes and containment predicate `76c18fb95d1f`. All are unchanged, so no port is owed. objectui's `sdui-parser/src/dashboard-widget-options.ts` changed (objectui#11389) to the five-key set this repo's copy already carries. ## Records that turned false at the new pin Three asserting records said something objectui changed. Only the false sentence of each is rewritten, and each new sentence names the objectui commit that changed it. Rollback for each: revert that docblock hunk alone (comment text, no schema or code); every other line of the record and of this PR stands without it. ### 1. `element:repeater` row (`ui/component.zod.ts`, the `ElementRepeaterPropsSchema` docblock) - **Old:** "NOT read at the pin, whatever a sibling might suggest: the node-level `dataSource` binding. This renderer is not wrapped in objectui's element-data-source gate, so the query keys above are the only way to aim it." And: "`object` is REQUIRED, as `element:number`'s is: without it the renderer never queries and shows its "No records" state (`:171-174`, `:209`) — indistinguishable from an object that really has no rows. The registration marks it required too." - **New:** "READ at this pin, since objectui#11880 (objectui `aaba8655a`, the repeater half): the node-level `dataSource` binding. The renderer resolves it through `useElementDataSource` (`:172-173`) … and reads it FIRST, the query keys above being its fallback." It then states the precedence: `object` is the binding's (`:192-195`); `filter` is this row's AND the binding's (`:180-188`); `sort` and `limit` take the binding's own value, then this row's key, then the binding's saved view (`:196-199`). The registration is marked with `elementDataSourceBlock` (`:311`). The `object` bullet now says the renderer takes `dataSource.object` first and this key second, and that with neither it never queries (`:234-237`, `:279`). - **What cannot be made true without a schema change:** the row still requires `object`, and the registration still marks it required because the row does (`:320-326`). So a node that names its object only through `dataSource` renders at this pin while this row refuses it. The text says so and names #11509 as the card that retires these flat keys. The schema is unchanged. - **Cited diff:** `git diff a58626c f0268ad -- packages/components/src/renderers/basic/data-list.tsx`, +99/-16, one commit (`aaba8655a`). The read points cited at `:171`, `:183`, `:148`, `:181` and `:182` were rewritten from `props.object` / `props.filter` / `props.sort` / `props.limit` to the resolved `object`, `scoped.filter`, `sort` and `limit`. ### 2. `action:button` row (`ui/component.zod.ts`, the `ActionButtonPropsSchema` docblock) - **Old:** "The registration's `inputs` (`:427-572`) publish twenty-seven of the twenty-nine keys … the two left unpublished are `endpoint` (refused here, above) and `undoable`, each still forwarded (`:215-318`), on objectui's measurement that the console's own `api` handler reads `target` and never `endpoint`, and that the runner offers Undo only with a host row stash this block never writes." - **New:** "The registration's `inputs` (`:507-658`) publish twenty-eight of the twenty-nine keys … twenty-seven since objectui#11168 slice 1, and `undoable` since objectui `f0496bdf1` … the one left unpublished is `endpoint` … `undoable` is published because the block now writes the row stash the runner's Undo needs: an `undoable` `operation: 'update'` of the record in scope carries that record as its Undo baseline (`withUndoBaseline`, `:72-81`, called at `:247-251`; objectui#11168, ruling B on objectui#11754), where at `a58626c88` it wrote none." - **Cited diff:** `git diff a58626c f0268ad -- packages/components/src/renderers/action/action-button.tsx`, +95/-9 (`f0496bdf1`, `cef0eeec0`). The row already declares `undoable`, so the accept set does not move. ### 3. `action:icon` row (`ui/component.zod.ts`, the `ActionIconPropsSchema` docblock) - **Old:** "`label` is read five times at this pin (four at `db11afd49`): `:109` …, `:165` (forwarded), `:281` (`aria-label`, falling back to `name`), `:290` (its first letter when no icon resolves) and `:296` / `:302` (the tooltip). `description` is the tooltip's fallback (`:302`) as well as forwarded (`:166`)." - **New:** "`label` is read six times at this pin (five at `a58626c88`, four at `db11afd49`)", with the five re-pointed and a sixth: "`:311` (the heading of the disabled-reason tooltip a predicate-disabled icon is wrapped in, new with objectui#11839, objectui `cef0eeec0`)". `description` is now the fallback of both tooltips (`:311`, `:324`). - **Cited diff:** `git diff a58626c f0268ad -- packages/components/src/renderers/action/action-icon.tsx`, +22/-0 (`cef0eeec0`). ## How the other citations were re-measured - **Method.** Each asserting record's cited objectui files were resolved against the tree at `a58626c88` and intersected with the range's 1061 changed paths. Every anchor in a changed file was mapped through `git diff -U0 a58626c f0268ad` and its text compared at both pins. A record whose cited files are all byte-identical (`git diff --quiet`) gets that sentence and no re-pointing. - **Changed cited files** (each named, with its objectui change, in its record's new hop sentence): `containers.tsx`, `record-related-list.tsx`, `record-quick-actions.tsx`, the four `action-*.tsx` renderers, `basic/data-list.tsx`, `data-objectstack/src/index.ts`, `previews/block-config.ts`, `previews/block-types.ts`, `ObjectGrid.tsx`, `data-table.tsx`, `plugin-kanban/src/index.tsx`, `ObjectCalendar.tsx`, `ObjectMap.tsx`, `plugin-gantt/src/index.tsx`, `ListView.tsx`, `ObjectView.tsx`, `ObjectTimeline.tsx`, `plugin-timeline/src/index.tsx`, `types/src/objectql.ts`, `types/src/zod/objectql.zod.ts`, `form.tsx`, `ActionRunner.ts`, `date-display.ts`, and the `en` / `zh` / `de` packs. - **Result.** About 185 anchor tokens in the records' current text were re-pointed. Every moved line is byte-identical at the new pin, except where objectui rewrote a line around the same read; those ranges were re-read and their hop sentence says so: - the `action:button` `params` statement (`180-183` -> `247-254`, now wrapped in `withUndoBaseline`); - the action containers' member label lines (now `DisabledReasonMenuLabel`, still `action.label || action.name`); - `formatDate` in `date-display.ts` (`445-480` -> `568-605`): objectui#11693 names an instant's day in the display zone, and its `short` / `relative` / default style handling reads unchanged. - **Records.** Each comment record gains a dated 2026-10-09 hop sentence and keeps its earlier history. The `FormField.span` describe changes its sha only: `WIDE_FIELD_TYPES`, the field-type alias table and `spanLadderFor` are byte-identical (`spanLadderFor` moved `204-231` -> `274-301`). - **The dated `view.zod.ts` map record** (`.objectui-sha` pin `53ded82b`) carries seven quoted anchors that `--verify-anchors` checks against the live pin. All seven moved byte-identical: `resolveListMapConfig` `146` -> `147`, `FLAT_MAP_CONFIG_SPELLING` `85` -> `86`, `case 'map':` `2300` -> `2340`, `ObjectMapConfigSchema` `2388` -> `2513` (still `.strict()`), `LIST_VIEW_LOCAL_OVERRIDES` `1437` -> `1506` (still without `map`), and the two `getMapConfig` lines `415` -> `449` and `420` -> `454`. The record gains a "RE-READ again at pin `f0268ad78`" sentence, the way earlier bumps recorded it. - **Counts.** Each was re-taken by the record's own method and reads as before: - the `keyboardNavigation` hit lines: 15, against 3 for `schema.editable`; - `ObjectKanban.tsx`'s `quickAdd` / `onQuickAdd`: 2 each, against 11 for `onCardClick`; 0 `kanban-ui` registrations; - the `ElementDataSourceGate` occurrences in the five `src/index.tsx` shells: 0, 3, 3, 3 and 4; - `ObjectMap.tsx`'s `schema.*` read set: the same 17 names at both pins. - **Corpus counts.** The six migration entries' counts were re-taken with `git grep -o -F`. That method first reproduced every `a58626c88` number: 7754 files, `timeout` 1431, `RuntimeConfig` 299, `resourceLimits` 2, `objectstack` 17468, `@objectstack/spec` 7246, `useState` 2491, `window` 4255, `period` 247, `interval` 200, `metrics` 401, `Span` 509, `SpanSchema` 57, `TTL` 184 and `tenant` 1319. - The new readings are 8234 files, `timeout` 1658, `RuntimeConfig` 337, `resourceLimits` 2 (the same two `packages/app-shell` prose hits), `objectstack` 17956, `@objectstack/spec` 7522, `useState` 2622, `window` 4430, `period` 249, `interval` 213, `metrics` 404, `TTL` 184 and `tenant` 1338. - All 99 checked tokens still read zero at both pins (the export lists of `plugin-lifecycle-advanced.zod.ts`, `tracing.zod.ts` and `metrics.zod.ts`, plus every named key), except `Span` / `SpanSchema`, which read 517 / 57. The eight new `Span` hits are each a `colSpan`. ## For the contract review: changed lines in `packages/spec/src/**` that are neither a comment nor a string fragment There are none. Measured mechanically: each changed file was parsed with the TypeScript compiler at `11d119ab18` and at the head, comments and JSDoc dropped, and every string or template literal replaced by one placeholder token. The remaining token sequences are identical in 13 of the 15 files. The other two, `18.system-metrics-jsdoc-durations-unit-in-key.ts` and `18.system-tracing-otel-exporter-durations-unit-in-key.ts`, each gain one `+` joining one new string fragment into their `reason` string; `migrations/registry.ts` mirrors both. No identifier, key, value or export moves. ## Console build (the local Console Pin Gate equivalent) Both steps ran under the verify lock. - **Dependency build.** `turbo run build --concurrency=2 --filter=@objectstack/client --filter=@objectstack/spec --filter=@objectstack/sdui-parser` ran 34 tasks and exited 0 in 4m50s on the shared box. - **Console build.** `pnpm objectui:build` exited 0, holding the lock 6m51s. - `OBJECTUI_ROOT` pointed at a scratch repository holding no refs, with objectui's object store as its alternate, so the build's `git worktree add` registered nothing in the sibling objectui checkout. - The log reports "Bundle canary 'import/jobs' present". - It reports "Single-zod canary: exactly one zod version literal {major:4,minor:6,patch:5}". - It reports **"Console bundle carries THIS tree's @objectstack/spec, and only it"**. - It reports "@objectstack/console dist ready (64028 KB) from objectui@f0268ad78485". - `check:console-sha` and `check:console-injection` ran against that dist (results below). ## Gates and tests (head `21da749e30`) Everything below ran at head `21da749e30`, the merge of `origin/main` (`fdfdd7e76d`), after a full workspace build at that head (72 turbo tasks, exit 0). Exit codes were captured before any pipe. - **Derived gates.** `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 127 commands from 22 changed paths against merge base `fdfdd7e76`. All 127 ran and exited 0. The `--ran` reconciliation reads "127 derived, 127 run, 0 NOT-MEASURED, 0 UNRUN". - **Verdict lines of the gates this bump exists for:** - `check:objectui-pin-citations`: "55 asserting objectui pin citation(s) match .objectui-sha (f0268ad), 144 historical citation(s) recorded and not checked, across 1922 spec source(s)." With `--verify-anchors`: "13 anchor content assertion(s) verified against objectui at f0268ad". - `check:sdui-lockstep`: "OK — this copy is byte-identical to objectui@f0268ad78485 … over 214 grammar line(s) [blob 0131f27cf86d], agrees on all 25 diagnostic code(s) and on the `not-a-container` predicate [blob 76c18fb95d1f]". - `check-sdui-manifest.mjs`: "present, intact (sha256 097ae3cbcf95…, 107 components) and recorded at the live objectui pin f0268ad…". - `check:console-sha`: "Console dist matches the objectui pin (objectui@f0268ad78485)". `check:console-injection`: "the dist carries this tree's copy, and not the published one". - `check-adr-0087-registration --base origin/main`: one declared-breaking changeset, carrying its disposition. `check-changeset-no-major --base origin/main`: "This diff introduces no `major` bump". - `check:generated`: "All 15 generated artifacts are up to date". `check:migration-registry`: "src/migrations/registry.ts is current (402 semantic, 247 retired-key, 222 retired-def)". `check:nul-bytes`: OK, no raw control bytes. - **Tests.** - `@objectstack/spec` vitest, both projects: 680 files, 19658 passed, 1 todo. `@objectstack/spec` typecheck: exit 0. - `@objectstack/sdui-parser`: 14 files, 225 passed; typecheck exit 0. - `@objectstack/lint` `validate-jsx-pages.production-witness.test.ts`: 5 passed. `@objectstack/metadata-protocol` `protocol.runtime-authoring-gate.test.ts`: 119 passed. - `@objectstack/cli`, unit tier (`sdui-manifest`, `console.sha-drift`, `validate-build-gate-parity`, `console-resolve`): 4 files, 102 passed. The integration tier is declared to CI: this diff touches no integration-tier file and no spawn entry. - **Lint, narrowed and proven.** ESLint over the 15 changed files under `packages/spec/src`: 0 errors, 0 warnings, 15 files by the `--format json` count. The population is the `eslint.config.mjs` block that lints every `ts` file (line 971). The config never enables type-aware linting (line 328), so this diff cannot move the verdict on any file it does not touch. The repo-wide `pnpm lint` is CI's run. ## Acceptance notes - **`element:record_picker` and `element:number` docblocks.** Their rows in `ui/component.zod.ts` cite no `.objectui-sha` value, so no pin gate reads them and they are outside this PR's file surface. Some of their sentences still describe the flat query keys as read, which is false at the new pin (objectui `74add0c12`). Their rewrite goes with the retirement of those keys. Carrier: #11509. - **Release coupling.** From this merge until the #11509 retirement lands, the Console ignores `element:record_picker` / `element:number` flat `object` / `filter` / `sort` / `limit` while `@objectstack/spec` still accepts them. The ruling ships both in the same release. No example or shipped page in this repo writes those flat keys. The only page that places an `element:record_picker`, the showcase's `page-variables`, binds through `dataSource`. Test fixtures in `packages/lint` are not shipped pages. - **The repeater, spec side.** A repeater that names its object only through `dataSource` renders at the new pin, but its row still requires `object`. The docblock now says so. Relaxing the requirement, and any lint waiver, is a schema change that belongs to #11509. - **Studio's repeater inspector** (objectui `previews/block-config.ts:338-345`) still writes the flat `object` key. The renderer still reads it as the fallback, so nothing breaks at this pin. It becomes relevant when #11509 retires the key. Carrier: #11509. - **`scripts/build-console.sh` and linked worktrees.** The script accepts `OBJECTUI_ROOT` only if `$OBJECTUI_ROOT/.git` is a directory (line 162). A linked objectui worktree has a `.git` file, so the script silently falls back to the sibling `../objectui` checkout and registers its build worktree there. `bump-objectui.sh` checks the same thing with `-e` (line 162), so it accepts a linked worktree. I built from a scratch repository whose object store points at objectui's as an alternate, so nothing was registered in the shared checkout. Noted here only, carrier: none. - **`main` moved after the single merge.** After `21da749e30`, `origin/main` gained `ac8f2c560` (#22380), `a55fdc470` (#22395), `b1f7a7a73c` (#22392), `d8d0711176` (#22393) and `83e7ae93ad` (#22403). Of this PR's files, only `packages/spec/src/migrations/registry.ts` changes there: #22380's hunks sit at old lines 5659, 6403, 13522, 13550 and 20651, and this PR's sit between 15141 and 20302. `git merge-tree --write-tree HEAD origin/main` exits 0 with no conflict. Per the dispatch, there is no second merge. - **Citation count.** The dispatch estimated 54 asserting citations in 12 files. The gate counts 55, and they sit in 15 files (14 hand-edited files plus the generated `registry.ts`). - **Writes.** This PR was opened through `scripts/pm/fleet-write/dispatch.mjs` (`pr_create`, draft). Its assignee was set through `scripts/pm/label-write.mjs`. No label was written, `skip-changeset` included. No MCP write was used. --- _Generated by [Claude Code](https://claude.ai/code/session_0115N1oNnQS5WqofZ2DzaT3q)_ --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: os-sales <sales@objectstack.ai>
1 parent 806b03e commit bf492c8

22 files changed

Lines changed: 809 additions & 278 deletions

‎.changeset/console-f0268ad78485.md‎

Lines changed: 154 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
The spec's objectui citations, and the shipped description text that names the `.objectui-sha` pin (the `FormField.span` describe and six migration-entry descriptions), are re-measured against the new console pin, objectui `f0268ad78485`.
6+
7+
Clause-②: no
8+
9+
Every anchor was mapped through the objectui diff `a58626c88dc8..f0268ad78485`, 1061 paths over 170 commits. Where a cited line moved with its text byte-identical, the record is re-pointed and its new hop sentence says by how much. Where objectui rewrote a cited line around the same read, the range is re-read and the hop sentence says so.
10+
11+
Three records said something that is false at the new pin, and only those sentences are rewritten:
12+
13+
- `element:repeater` reads the node-level `dataSource` binding first since objectui#11880 (objectui `aaba8655a`), with its flat `object` / `filter` / `sort` / `limit` keys as the fallback. The row's docblock said the binding was not read. It now states the precedence the renderer applies, and that a node naming its object only through `dataSource` renders while this row still requires `object`: that requirement can follow the renderer only by a schema change, which is objectstack#11509's.
14+
- `action:button` now publishes `undoable` and hands the runner the record in scope as its Undo baseline (objectui#11168, objectui `f0496bdf1`). The row's docblock said the input stayed unpublished because the block wrote no row stash.
15+
- `action:icon` reads `label` six times, not five: the tooltip of a predicate-disabled icon takes it as its heading (objectui#11839, objectui `cef0eeec0`).
16+
17+
The `FormField.span` describe changes its sha only: `WIDE_FIELD_TYPES`, the field-type alias table and `spanLadderFor` are byte-identical at the new pin. The six migration entries' corpus counts were re-taken with `git grep -o -F`, the method that first reproduced every `a58626c88dc8` number. The corpus is now 8234 tracked files. All 99 checked tokens still read zero, except `Span` / `SpanSchema`, which read 517 / 57: the eight new `Span` hits are each a `colSpan`.
18+
19+
No key, default, enum member or export moves.

‎.objectui-sha‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
a58626c88dc85954bd0af24f16ebb69454c03eee
1+
f0268ad784854568aa58a2aa791f6a7502259186

‎content/docs/references/ui/view.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ Column footer summary configuration
213213
| **required** | `boolean` | optional | Required override |
214214
| **hidden** | `boolean` | optional | Hidden override |
215215
| **colSpan** | `integer` | optional | Absolute column span (1-4). The renderer clamps it to the form grid's current column count, so the cell starts at a real column boundary at every surface width and never overflows (`colSpan: 4` in a 3-column grid renders as 3); a `colSpan` within the column count renders as authored, and `colSpan: 1` emits no span class at all. |
216-
| **span** | `Enum<'auto' \| 'full'>` | optional (default: `"auto"`) | Relative field width. 'auto' (default — omit it): the renderer sizes the field from its widget type × the current column count — at the pin this repo builds against (`.objectui-sha` = `a58626c88dc8`), only textarea, markdown, html, richtext and repeater resolve to the full column count (repeater reaches it through the wide `field:grid` widget it maps to). 'full': resolves to the form grid's full column count. How far down the container-query tiers that span is emitted is the renderer's, not this key's: at that same pin the renderer emits one clamped col-span class per multi-column tier (`@md:col-span-2 @2xl:col-span-3` for a 3-column grid), so the field takes the whole row at every multi-column tier, not just the widest. |
216+
| **span** | `Enum<'auto' \| 'full'>` | optional (default: `"auto"`) | Relative field width. 'auto' (default — omit it): the renderer sizes the field from its widget type × the current column count — at the pin this repo builds against (`.objectui-sha` = `f0268ad78485`), only textarea, markdown, html, richtext and repeater resolve to the full column count (repeater reaches it through the wide `field:grid` widget it maps to). 'full': resolves to the form grid's full column count. How far down the container-query tiers that span is emitted is the renderer's, not this key's: at that same pin the renderer emits one clamped col-span class per multi-column tier (`@md:col-span-2 @2xl:col-span-3` for a 3-column grid), so the field takes the whole row at every multi-column tier, not just the widest. |
217217
| **widget** | `string` | optional | Custom widget/component name (overrides type-based inference) |
218218
| **language** | `string` | optional | Code editor language (for type=code) |
219219
| **keyField** | `{ field?: string; label?: string \| Record<string, string>; placeholder?: string \| Record<string, string>; helpText?: string \| Record<string, string>; … }` | optional | Key column config for record-typed fields |
@@ -343,7 +343,7 @@ Form-view select option — the object-field option shape minus the per-option `
343343
| **required** | `boolean` | optional | Required override |
344344
| **hidden** | `boolean` | optional | Hidden override |
345345
| **colSpan** | `integer` | optional | Absolute column span (1-4). The renderer clamps it to the form grid's current column count, so the cell starts at a real column boundary at every surface width and never overflows (`colSpan: 4` in a 3-column grid renders as 3); a `colSpan` within the column count renders as authored, and `colSpan: 1` emits no span class at all. |
346-
| **span** | `Enum<'auto' \| 'full'>` | optional (default: `"auto"`) | Relative field width. 'auto' (default — omit it): the renderer sizes the field from its widget type × the current column count — at the pin this repo builds against (`.objectui-sha` = `a58626c88dc8`), only textarea, markdown, html, richtext and repeater resolve to the full column count (repeater reaches it through the wide `field:grid` widget it maps to). 'full': resolves to the form grid's full column count. How far down the container-query tiers that span is emitted is the renderer's, not this key's: at that same pin the renderer emits one clamped col-span class per multi-column tier (`@md:col-span-2 @2xl:col-span-3` for a 3-column grid), so the field takes the whole row at every multi-column tier, not just the widest. |
346+
| **span** | `Enum<'auto' \| 'full'>` | optional (default: `"auto"`) | Relative field width. 'auto' (default — omit it): the renderer sizes the field from its widget type × the current column count — at the pin this repo builds against (`.objectui-sha` = `f0268ad78485`), only textarea, markdown, html, richtext and repeater resolve to the full column count (repeater reaches it through the wide `field:grid` widget it maps to). 'full': resolves to the form grid's full column count. How far down the container-query tiers that span is emitted is the renderer's, not this key's: at that same pin the renderer emits one clamped col-span class per multi-column tier (`@md:col-span-2 @2xl:col-span-3` for a 3-column grid), so the field takes the whole row at every multi-column tier, not just the widest. |
347347
| **widget** | `string` | optional | Custom widget/component name (overrides type-based inference) |
348348
| **language** | `string` | optional | Code editor language (for type=code) |
349349
| **keyField** | `{ field?: string; label?: string \| Record<string, string>; placeholder?: string \| Record<string, string>; helpText?: string \| Record<string, string>; … }` | optional | Key column config for record-typed fields |

‎packages/sdui-parser/objectui-lockstep.json‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
],
77
"objectui": {
88
"repo": "https://github.com/objectstack-ai/objectui.git",
9-
"rev": "a58626c88dc85954bd0af24f16ebb69454c03eee",
10-
"revDate": "2026-10-06T15:08:56+00:00",
9+
"rev": "f0268ad784854568aa58a2aa791f6a7502259186",
10+
"revDate": "2026-10-09T01:49:47+00:00",
1111
"source": "packages/sdui-parser/src",
1212
"files": [
1313
"packages/sdui-parser/src/body-dialect.ts",
@@ -21,7 +21,7 @@
2121
"packages/sdui-parser/src/validate.ts"
2222
]
2323
},
24-
"recordedAgainstPin": "a58626c88dc85954bd0af24f16ebb69454c03eee",
24+
"recordedAgainstPin": "f0268ad784854568aa58a2aa791f6a7502259186",
2525
"grammarRegion": {
2626
"file": "packages/sdui-parser/src/parse.ts",
2727
"delimiter": "/* ---------------------- the JS literal subset (#6614) ---------------------- */",

‎packages/spec/src/data/api-methods-batch-conformance.test.ts‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,19 @@ const SINGLE_RECORD_WRITE_ONLY: Record<string, string> = {
6161
// `revoked` on ONE key. The multi-select surface this rule protects does not
6262
// exist for API keys, and the shape a future one would take does not need
6363
// `bulk` either — both read off the console build this release pins
64-
// (`.objectui-sha` = `a58626c88`, re-read there 2026-10-06:
64+
// (`.objectui-sha` = `f0268ad78`, re-read there 2026-10-09:
65+
// in `packages/plugin-grid`, on the hop off `a58626c88`, `ObjectGrid.tsx`
66+
// changed again (85 insertions, 27 deletions: objectui#11880 item 5's grid
67+
// filter hand-off, objectui#6152 rounds 7 and 10, objectui#11817's link cell,
68+
// objectui#11809's option order and objectui#11689's boolean face), none of
69+
// it inside the selection block and no hunk naming a bulk action, a delete
70+
// affordance or `operations`, so the block only MOVED by +54:
71+
// `ObjectGrid.tsx:4867-4894` here (`4813-4840` at `a58626c88`), still hashing
72+
// to `c88443302d40c2db739ddb235470bafa29056e2e` (hash-object of the block),
73+
// re-READ with the same reading below; `hooks/useBulkExecutor.ts` is
74+
// byte-identical to `a58626c88` and its `:298-303` still hashes to
75+
// `01083348330f10a201cdf1078b4c21c236402b6a`. At `a58626c88`, re-read there
76+
// 2026-10-06:
6577
// every objectui file this record cites is byte-identical across the hop from
6678
// `0abd4f9f8` (`git diff --quiet`), so every anchor held unmoved.
6779
// At `0abd4f9f8`, re-read there 2026-10-05:

‎packages/spec/src/kernel/functional-completeness.ts‎

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,16 @@ export function checkFieldCompleteness(def: unknown): CompletenessFinding[] {
298298
* English sentence. So the loss is total rather than partial — every
299299
* record, on every object — and the author is told at render time as well
300300
* as here. Both reads hold at the pin this repo builds against
301-
* (`.objectui-sha` = `a58626c88`, re-read there 2026-10-06:
301+
* (`.objectui-sha` = `f0268ad78`, re-read there 2026-10-09:
302+
* on the hop off `a58626c88` `ObjectCalendar.tsx` changed in one comment line
303+
* only, line for line (`:1250`, objectui `c0862c1cc` re-citing the installed spec
304+
* as 17.7.0), so `getCalendarConfig` `:294`, the `if (!calendarConfig)` arm
305+
* `:1296` and its `tt(…)` lines `:1303-1304` did not move and are byte-identical;
306+
* `ListView.tsx` changed (+286/-46: objectui#11984, objectui#6152 round 12,
307+
* objectui#11860, objectui#11943, objectui#11925, objectui#11816, objectui#11810
308+
* and objectui#11687), every `case` arm byte-identical and moved by 83; and the
309+
* `en` / `zh` / `de` packs changed (17 commits) but not their
310+
* `calendar.configRequired` strings. At `a58626c88`, re-read there 2026-10-06:
302311
* on the hop off `0abd4f9f8` `ObjectCalendar.tsx` and `ListView.tsx` are
303312
* byte-identical (`git diff --quiet`), so every anchor held unmoved, and the
304313
* `en` / `zh` / `de` packs changed (objectui#11658, objectui#11659,
@@ -375,7 +384,12 @@ export function checkFieldCompleteness(def: unknown): CompletenessFinding[] {
375384
* This repo already records the same deletion one door over: the #13817
376385
* check in `../ui/view.zod.ts` names objectui#7029 as its runtime half.
377386
* - `gantt` → NO fallback, and no silence [#19630]. Measured at the pin
378-
* this repo builds against (`.objectui-sha` = `a58626c88`, re-read there 2026-10-06:
387+
* this repo builds against (`.objectui-sha` = `f0268ad78`, re-read there 2026-10-09:
388+
* on the hop off `a58626c88` `ObjectGantt.tsx` is byte-identical
389+
* (`git diff --quiet`), so `getGanttConfig` `:610` and the `if (!ganttConfig)`
390+
* arm `:2307` did not move, and `ListView.tsx` changed (+286/-46, the same eight
391+
* objectui changes as above) with its `case 'gantt'` byte-identical, moved by 83.
392+
* At `a58626c88`, re-read there 2026-10-06:
379393
* every objectui file this record cites is byte-identical across the hop from
380394
* `0abd4f9f8` (`git diff --quiet`), so every anchor held unmoved.
381395
* At `0abd4f9f8`, re-read there 2026-10-05:
@@ -443,7 +457,15 @@ export function checkFieldCompleteness(def: unknown): CompletenessFinding[] {
443457
* requires. So the view does not draw a blank chart: it refuses, by name.
444458
* - `timeline` → date axis: NO fallback [#19630]; title: `titleField || 'name'`,
445459
* which still stands. Measured at the same pin (`.objectui-sha` =
446-
* `a58626c88`, re-read there 2026-10-06:
460+
* `f0268ad78`, re-read there 2026-10-09:
461+
* on the hop off `a58626c88` `ObjectTimeline.tsx` changed in three comments only
462+
* (+19/-8, objectui#6152 round 12: the nested block's type is the spec slot by
463+
* reference, its legacy `dateField` refused by name while the read stays), so its
464+
* start-date chain and its `if (!hasAuthoredItems && !startDateField)` refusal
465+
* arm are byte-identical, MOVED `:578-580` → `:589-591` and `:941` → `:952`, and
466+
* `ListView.tsx` changed (+286/-46) with its `case 'timeline'` and
467+
* `resolveTimelineDateBinding` byte-identical, moved by 83 and by 13. At
468+
* `a58626c88`, re-read there 2026-10-06:
447469
* on the hop off `0abd4f9f8` `ObjectTimeline.tsx` changed (+39/-9:
448470
* objectui#11675 starts the date buckets' week on the display locale's first
449471
* day, and objectui#11676 heads a past date "Earlier" and retires the
@@ -504,7 +526,15 @@ export function checkFieldCompleteness(def: unknown): CompletenessFinding[] {
504526
* block does render; the warning still fires there, because the block the
505527
* view TYPE names is the one that is missing. Unchanged by this row.
506528
* - `map` → NO fallback, and no silence [#19630]. Measured at the same
507-
* pin (`.objectui-sha` = `a58626c88`, re-read there 2026-10-06:
529+
* pin (`.objectui-sha` = `f0268ad78`, re-read there 2026-10-09:
530+
* on the hop off `a58626c88` `ObjectMap.tsx` changed (85 insertions, 4 deletions,
531+
* objectui#11819: without WebGL2 the map lists its records instead of crashing),
532+
* which touches no coordinate read: `getMapConfig` and `hasCoordinateBinding` are
533+
* byte-identical and moved `:387` → `:421` and `:493` → `:527` under the WebGL2
534+
* probe above them, and the refusal arm is byte-identical, moved `:1291` →
535+
* `:1337`; and `ListView.tsx` changed (+286/-46) with its `case 'map'`
536+
* byte-identical, moved by 83, and `resolveListMapConfig` byte-identical, moved
537+
* `:146` → `:147`. At `a58626c88`, re-read there 2026-10-06:
508538
* every objectui file this record cites is byte-identical across the hop from
509539
* `0abd4f9f8` (`git diff --quiet`), so every anchor held unmoved.
510540
* At `0abd4f9f8`, re-read there 2026-10-05:

‎packages/spec/src/migrations/entries/semantic/18.kernel-health-check-and-hot-reload-durations-unit-in-key.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,13 @@ export const entry: SemanticMigration = {
5252
+ 'the audience that does not parse. Measured on 884e8347d: the only in-repo readers are '
5353
+ 'packages/core/src/health-monitor.ts and packages/core/src/hot-reload.ts, both moved in '
5454
+ 'this same change; and the pinned objectui checkout — the pin this repo builds '
55-
+ 'against, `.objectui-sha` = `a58626c88dc85954bd0af24f16ebb69454c03eee` — names '
55+
+ 'against, `.objectui-sha` = `f0268ad784854568aa58a2aa791f6a7502259186` — names '
5656
+ 'neither def and neither key: all thirteen exports of plugin-lifecycle-advanced.zod.ts and '
57-
+ 'the string debounceDelay each occur 0 times across its 7754 tracked files (0 across the 7650 at 0abd4f9f8, the 7632 at 9dfaca654, the 7579 at 2e818d0b5, the 10267 at ab1879721, the 10071 at 89cad75d5, the 9912 at 31971ff1e, the 9800 at e420df310, the 9546 at db11afd49, the 9283 at dd3f7e1be, the '
57+
+ 'the string debounceDelay each occur 0 times across its 8234 tracked files (0 across the 7754 at a58626c88, the 7650 at 0abd4f9f8, the 7632 at 9dfaca654, the 7579 at 2e818d0b5, the 10267 at ab1879721, the 10071 at 89cad75d5, the 9912 at 31971ff1e, the 9800 at e420df310, the 9546 at db11afd49, the 9283 at dd3f7e1be, the '
5858
+ '8512 at f8a9d0fb0 and the 8303 at 62597c588 too), against lit '
5959
+ 'controls objectstack 12966 and @objectstack/spec 4997 on the same corpus at 87af769e9, '
6060
+ 'which re-count to 13125 and 5043 respectively at 62597c588, to 13347 and 5123 at '
61-
+ 'f8a9d0fb0, to 13745 and 5466 at dd3f7e1be, to 14704 and 5545 at db11afd49, to 15352 and 6024 at e420df310, to 15691 and 6206 at 31971ff1e, to 16044 and 6461 at 89cad75d5, to 16377 and 6665 at ab1879721, to 17227 and 7134 at 2e818d0b5, to 17313 and 7186 at 9dfaca654, to 17390 and 7209 at 0abd4f9f8 and to 17468 and 7246 at this pin (git grep -o -F, the method that reproduces '
61+
+ 'f8a9d0fb0, to 13745 and 5466 at dd3f7e1be, to 14704 and 5545 at db11afd49, to 15352 and 6024 at e420df310, to 15691 and 6206 at 31971ff1e, to 16044 and 6461 at 89cad75d5, to 16377 and 6665 at ab1879721, to 17227 and 7134 at 2e818d0b5, to 17313 and 7186 at 9dfaca654, to 17390 and 7209 at 0abd4f9f8, to 17468 and 7246 at a58626c88 and to 17956 and 7522 at this pin (git grep -o -F, the method that reproduces '
6262
+ 'every earlier count).',
6363
acceptanceCriteria:
6464
'Every producer and reader of a PluginHealthCheck spells intervalMs and timeoutMs, and every '

‎packages/spec/src/migrations/entries/semantic/18.kernel-runtime-config-timeout-unit-in-key.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ export const entry: SemanticMigration = {
4646
+ 'spells timeout 0 times; outside the zod file and its test the only live occurrences are the '
4747
+ 'generated rows in content/docs/references/kernel/plugin-security-advanced.mdx, which this '
4848
+ 'rename regenerates. The pinned objectui checkout — this is the pin we build against, '
49-
+ '`.objectui-sha` = `a58626c88dc85954bd0af24f16ebb69454c03eee`, re-read from this tree — '
49+
+ '`.objectui-sha` = `f0268ad784854568aa58a2aa791f6a7502259186`, re-read from this tree — '
5050
+ 'spells resourceLimits.timeout 0 times across '
51-
+ '7754 tracked files, against lit controls timeout 1431, RuntimeConfig 299 and resourceLimits '
52-
+ '2 on the same corpus (0 across 7650, and 1360 / 293 / 2, at 0abd4f9f8; 0 across 7632, and 1360 / 293 / 2, at 9dfaca654; 0 across 7579, and 1351 / 276 / 2, at 2e818d0b5; 0 across 10267, and 1348 / 273 / 2, at ab1879721; 0 across 10071, and 1331 / 273 / 2, at 89cad75d5; 0 across 9912, and 1303 / 273 / 2, at 31971ff1e; 0 across 9800, and 1293 / 273 / 2, at e420df310; 0 across 9546, and 1197 / 273 / 2, at db11afd49; 0 across 9283, and 1172 / 263 / 2, at dd3f7e1be; 0 across 8512, and 1096 / 245 / 2, at f8a9d0fb0; 0 across 8303, and '
51+
+ '8234 tracked files, against lit controls timeout 1658, RuntimeConfig 337 and resourceLimits '
52+
+ '2 on the same corpus (0 across 7754, and 1431 / 299 / 2, at a58626c88; 0 across 7650, and 1360 / 293 / 2, at 0abd4f9f8; 0 across 7632, and 1360 / 293 / 2, at 9dfaca654; 0 across 7579, and 1351 / 276 / 2, at 2e818d0b5; 0 across 10267, and 1348 / 273 / 2, at ab1879721; 0 across 10071, and 1331 / 273 / 2, at 89cad75d5; 0 across 9912, and 1303 / 273 / 2, at 31971ff1e; 0 across 9800, and 1293 / 273 / 2, at e420df310; 0 across 9546, and 1197 / 273 / 2, at db11afd49; 0 across 9283, and 1172 / 263 / 2, at dd3f7e1be; 0 across 8512, and 1096 / 245 / 2, at f8a9d0fb0; 0 across 8303, and '
5353
+ '1086 / 240 / 2, at 62597c588); both resourceLimits hits are prose in packages/app-shell recording '
5454
+ 'that objectui\'s own AppShellRuntimeConfig shares not one key with the spec\'s '
5555
+ 'RuntimeConfig, so nothing there authors this key and no pin bump is owed. ADR-0087.',

0 commit comments

Comments
 (0)