Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions .changeset/console-ab1879721595.md

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions .changeset/objectui-pin-citations-ab1879721595.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
'@objectstack/spec': patch
---

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 `ab1879721595`.

Clause-②: no

Several records were corrected rather than moved, because objectui changed what they describe on this hop.

- **`object-grid` `keyboardNavigation`.** The grid now reads the key (objectui#11068), so its describe drops the `[EXPERIMENTAL — not enforced]` marker and the sentence that said no renderer reads it and authoring it changes nothing. The describe now says what the grid does: the data cells become one Tab stop that the arrow keys, Home / End and Ctrl+Home / Ctrl+End move between. It is on by default when the grid renders editable, `true` turns it on for a read-only grid, and `false` turns it off on an editable one.
- **`object-grid` `emptyState`.** Its record now says the grid resolves `title` and `message` against the display locale (objectui#11227), so an inline locale map draws.
- **`ActionSchema.outcomeMessages`.** The console reader landed (objectui#11344). The key's liveness row is now `live`, and authoring it no longer draws the "the console does not show outcome copy yet" author warning. The `successMessage` row records that `${result.*}` is interpolated.
- **The four `action:*` rows.** The action renderers forward `outcomeMessages` to the action runner. The `action:button` and `action:icon` rows record it as a key the renderer forwards and the row does not declare. The `action:group` and `action:menu` rows record that each member's own `outcomeMessages` rides the member forward.

Every other anchor either held on a byte-identical file or moved with its cited text byte-identical. The corpus counts in the six migration entries were re-taken with the method that reproduces the previous pin's numbers. No key, default, enum member or export moves.
2 changes: 1 addition & 1 deletion .objectui-sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
89cad75d55702cc4f267bead5bf267de575d5842
ab187972159583b595facdcae3c73b50f6f312e9
2 changes: 1 addition & 1 deletion content/docs/references/ui/component.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,7 @@ Sort field and direction pair
| **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; preventNavigation?: boolean; openNewTab?: boolean; size?: Enum<'auto' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'>; … }` | optional | Row-click navigation config — the same block `ListViewSchema.navigation` declares (`{ mode, size, openNewTab, preventNavigation }`) |
| **editable** | `boolean` | optional | Enable inline cell editing |
| **singleClickEdit** | `boolean` | optional | Enter cell edit on single click (default true when editable) |
| **keyboardNavigation** | `boolean` | optional | [EXPERIMENTAL — not enforced] Arrow-key cell navigation on the WAI-ARIA grid pattern. Defaults to on when `editable` is set; a read-only grid keeps its Tab behaviour unless this is `true`. No renderer reads it yet: it is declared ahead of the grid's keyboard-navigation build, so authoring it changes nothing today |
| **keyboardNavigation** | `boolean` | optional | Arrow-key cell navigation on the WAI-ARIA grid pattern: the grid's data cells become one Tab stop that the arrow keys, Home / End and Ctrl+Home / Ctrl+End move between. Defaults to on when the grid renders editable — `editable` set and the viewer allowed to edit; a grid that renders read-only keeps every cell its own Tab stop unless this is `true`, and `false` turns it off on an editable grid |
| **resizable** | `boolean` | optional | Allow column resize (the renderer default is on) |
| **resizableColumns** | `never` | optional | [REMOVED] `object-grid` property `resizableColumns` was removed in @objectstack/spec 17.7.0 (ADR-0049) — it was the legacy second spelling of `resizable`, read only when `resizable` was absent, so one switch had two spellings and a grid authoring both silently ignored this one. Use `resizable`. Rename the key; the value (a boolean) is unchanged. Run `os migrate meta --from 17` to list the mechanical edits for existing sources; apply them by hand. |
| **reorderableColumns** | `boolean` | optional | Allow column drag-reorder |
Expand Down
4 changes: 2 additions & 2 deletions content/docs/references/ui/view.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ Column footer summary configuration
| **required** | `boolean` | optional | Required override |
| **hidden** | `boolean` | optional | Hidden override |
| **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. |
| **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` = `89cad75d5570`), 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. |
| **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` = `ab1879721595`), 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. |
| **widget** | `string` | optional | Custom widget/component name (overrides type-based inference) |
| **language** | `string` | optional | Code editor language (for type=code) |
| **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 |
Expand Down Expand Up @@ -343,7 +343,7 @@ Form-view select option — the object-field option shape minus the per-option `
| **required** | `boolean` | optional | Required override |
| **hidden** | `boolean` | optional | Hidden override |
| **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. |
| **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` = `89cad75d5570`), 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. |
| **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` = `ab1879721595`), 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. |
| **widget** | `string` | optional | Custom widget/component name (overrides type-based inference) |
| **language** | `string` | optional | Code editor language (for type=code) |
| **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 |
Expand Down
6 changes: 3 additions & 3 deletions packages/sdui-parser/objectui-lockstep.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
],
"objectui": {
"repo": "https://github.com/objectstack-ai/objectui.git",
"rev": "89cad75d55702cc4f267bead5bf267de575d5842",
"revDate": "2026-10-02T06:25:01+00:00",
"rev": "ab187972159583b595facdcae3c73b50f6f312e9",
"revDate": "2026-10-03T17:12:55+00:00",
"source": "packages/sdui-parser/src",
"files": [
"packages/sdui-parser/src/body-dialect.ts",
Expand All @@ -21,7 +21,7 @@
"packages/sdui-parser/src/validate.ts"
]
},
"recordedAgainstPin": "89cad75d55702cc4f267bead5bf267de575d5842",
"recordedAgainstPin": "ab187972159583b595facdcae3c73b50f6f312e9",
"grammarRegion": {
"file": "packages/sdui-parser/src/parse.ts",
"delimiter": "/* ---------------------- the JS literal subset (#6614) ---------------------- */",
Expand Down
14 changes: 6 additions & 8 deletions packages/spec/liveness/action.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,14 @@
},
"successMessage": {
"status": "live",
"note": "objectui toast. 2026-10-01 (#21095): the describe() now also declares that the message may interpolate ${result.*} on a type:'api' / type:'script' action (the scope `onSuccess.navigate` declares) and that it is the fallback when a declared `outcomeMessages` entry does not match the response's `outcome`. Those two halves are declared ahead of their reader, by ruling A on objectstack-ai/cloud#2315 (spec first, then the console, then cloud): measured at objectui @e420df31 packages/core/src/actions/ActionRunner.ts#handlePostExecution, the toast is `result.data.message || action.successMessage || runner default` with no interpolation, so a ${result.X} token renders literally until objectstack-ai/objectui#11344 lands (re-read unchanged at objectui @31971ff1). The KEY stays `live` — the toast reads it — and a row's verdict is per key, so the interpolation half is recorded here and on the `outcomeMessages` row rather than by re-grading a working key. Census when declared: zero successMessage values in this repo or objectui carry a `${` token, so nothing that already renders changes meaning."
"note": "objectui toast. 2026-10-01 (#21095): the describe() now also declares that the message may interpolate ${result.*} on a type:'api' / type:'script' action (the scope `onSuccess.navigate` declares) and that it is the fallback when a declared `outcomeMessages` entry does not match the response's `outcome`. Those two halves are declared ahead of their reader, by ruling A on objectstack-ai/cloud#2315 (spec first, then the console, then cloud): measured at objectui @e420df31 packages/core/src/actions/ActionRunner.ts#handlePostExecution, the toast is `result.data.message || action.successMessage || runner default` with no interpolation, so a ${result.X} token renders literally until objectstack-ai/objectui#11344 lands (re-read unchanged at objectui @31971ff1). The KEY stays `live` — the toast reads it — and a row's verdict is per key, so the interpolation half is recorded here and on the `outcomeMessages` row rather than by re-grading a working key. Census when declared: zero successMessage values in this repo or objectui carry a `${` token, so nothing that already renders changes meaning. Re-read at objectui @ab1879721595 (2026-10-03): objectui#11344 landed in the pin's range — packages/core/src/actions/ActionRunner.ts#composeSuccessMessage fills `${result.*}` in the message it shows and uses `successMessage` as the fallback after a matching `outcomeMessages` entry, so both halves declared ahead of their reader are read; `result.data.message` is no longer a rung."
},
"outcomeMessages": {
"status": "planned",
"verifiedAt": "2026-10-01",
"evidenceScope": "in-repo",
"authorWarn": true,
"authorHint": "The console does not show outcome copy yet: until its reader ships, a successful action still toasts `successMessage` (or the server's own message), whatever `outcome` the handler returns. Keep the map — it is accepted, validated and translated today — and keep a `successMessage` as the fallback.",
"evidence": "packages/spec/src/ui/action.zod.ts#refuseInertOutcomeMessages (authoring-time: the map is refused on a type with no server response to report an outcome and beside `resultDialog`, which suppresses the toast; the declarative-update table refuses it beside `operation: 'update'`); packages/spec/src/system/i18n-resolver.ts#translateAction (the REST localization seam overlays each DECLARED outcome's translated copy at the action's one address: `_actions.<a>.outcomeMessages.<outcome>` for a bound action, `globalActions` for an object-less one); packages/cli/src/utils/i18n-extract.ts#pushActionOutcomeMessages (one translation key per declared outcome, beside `successMessage`)",
"note": "PLANNED, carrier objectstack-ai/objectui#11344 (the console's success toast picks `outcomeMessages[result.outcome]`, interpolates ${result.*}, falls back to `successMessage`, then the runner default). Declared by ruling A on objectstack-ai/cloud#2315 (maintainer 2026-10-01), the mechanism half of ruling B there — the server returns facts, the console composes the message in the user's locale — landing spec+client first (#21095), the console second, cloud's producers LAST. Measured at objectui @e420df31: packages/core/src/actions/ActionRunner.ts#handlePostExecution composes the toast from `result.data.message || action.successMessage || runner default` and reads no `outcomeMessages`; re-read unchanged at objectui @31971ff1 (the `.objectui-sha` pin after the base merge), and objectui main 3c3ce15 has no reader either. The in-repo pieces cited in `evidence` validate, translate and extract the copy; none of them SELECTS or shows it, which is what a `live` verdict would claim. Flip to `live` on the commit that cites the console reader at the `.objectui-sha` pin — and drop `authorWarn` / `authorHint` in that same edit: a warned `live` row reds this gate (#21127)."
"status": "live",
"verifiedAt": "2026-10-03",
"evidenceScope": "cross-repo",
"evidence": "objectui @ab1879721595: packages/core/src/actions/ActionRunner.ts#composeSuccessMessage — the success toast's text: `outcomeMessages[outcome]` off the handler's return value, then `successMessage`, then the runner's default text, with `${result.*}` filled in whichever is shown (called from `handlePostExecution`); objectui @ab1879721595: packages/components/src/renderers/action/action-button.tsx#ActionButtonRenderer — forwards the key to the runner, and `action:bar` spreads a registered action onto `action:button` whole (`action-icon.tsx`, `action-group.tsx` and `action-menu.tsx` forward it too); packages/spec/src/ui/action.zod.ts#refuseInertOutcomeMessages (authoring-time: the map is refused on a type with no server response to report an outcome and beside `resultDialog`, which suppresses the toast; the declarative-update table refuses it beside `operation: 'update'`); packages/spec/src/system/i18n-resolver.ts#translateAction (the REST localization seam overlays each DECLARED outcome's translated copy at the action's one address: `_actions.<a>.outcomeMessages.<outcome>` for a bound action, `globalActions` for an object-less one); packages/cli/src/utils/i18n-extract.ts#pushActionOutcomeMessages (one translation key per declared outcome, beside `successMessage`)",
"note": "LIVE from the `.objectui-sha` pin ab1879721595 (2026-10-03): the console reader objectstack-ai/objectui#11344 (objectui c476be0e0) is in the pin's range 89cad75d5570..ab1879721595, so the flip this row prescribed lands on the pin bump that carries it, with `authorWarn` / `authorHint` dropped in the same edit. At 89cad75d5570 no objectui source outside `packages/types` named the key (0 hit lines), and `handlePostExecution` toasted `result.data.message || action.successMessage || runner default`; at ab1879721595 the server-message rung is gone and `composeSuccessMessage` selects the outcome copy. An `outcome` reaches it only from a handler that returns one, which a `script` handler's author writes beside the map and an `api` target returns in its body. History: PLANNED, carrier objectstack-ai/objectui#11344 (the console's success toast picks `outcomeMessages[result.outcome]`, interpolates ${result.*}, falls back to `successMessage`, then the runner default). Declared by ruling A on objectstack-ai/cloud#2315 (maintainer 2026-10-01), the mechanism half of ruling B there — the server returns facts, the console composes the message in the user's locale — landing spec+client first (#21095), the console second, cloud's producers LAST. Measured at objectui @e420df31: packages/core/src/actions/ActionRunner.ts#handlePostExecution composes the toast from `result.data.message || action.successMessage || runner default` and reads no `outcomeMessages`; re-read unchanged at objectui @31971ff1 (the `.objectui-sha` pin after the base merge), and objectui main 3c3ce15 has no reader either. The in-repo pieces cited in `evidence` validate, translate and extract the copy; none of them SELECTS or shows it, which is what a `live` verdict would claim. Flip to `live` on the commit that cites the console reader at the `.objectui-sha` pin — and drop `authorWarn` / `authorHint` in that same edit: a warned `live` row reds this gate (#21127)."
},
"errorMessage": {
"status": "live",
Expand Down
2 changes: 1 addition & 1 deletion packages/spec/liveness/state-counts/action.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ committed anywhere: `check:liveness` sums the shards when it reads them.

| Type | live | exp | elsewhere | dead | planned | classified |
|---|---|---|---|---|---|---|
| `action` | 45 | 0 | 0 | 4 | 1 | 50 |
| `action` | 46 | 0 | 0 | 4 | 0 | 50 |
Loading
Loading