Skip to content

Commit 68ec6ba

Browse files
Merge branch 'main' into claude/issue-26073-north-star-one-referent
2 parents 43cb2c7 + a8ee8a2 commit 68ec6ba

262 files changed

Lines changed: 11671 additions & 6183 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@object-ui/i18n': patch
3+
---
4+
5+
feat(i18n): the rest of the Studio strings and the metadata preview panes move into the `studio` namespace
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Fifth slice of the move ruled on objectstack#25922** (6431032267, Q2 = A; kept by 6431234244). Every remaining `engine.studio.*` key except `engine.studio.inspector.*`, and the preview panes' `engine.*Preview.*` and `engine.previewShell.*` keys (the doc, book, flow, page-block and translation previews stay for a later slice), 596 keys in both `en` and `zh`, now live in `@object-ui/i18n/namespaces/studio` instead of `@object-ui/app-shell`'s private table, text unchanged. Studio reads the same strings; a Chinese viewer sees these labels in English for the moment the `zh` catalogue takes to load, as with the earlier slices. No export changes.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@objectstack/plugin-security': patch
3+
---
4+
5+
fix(plugin-security): the security plugin's metadata-derived caches now invalidate on a runtime metadata publish
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The security plugin keeps the per-object inputs it derives from metadata for reuse across requests. Those caches are now dropped whenever metadata changes at runtime, so the next request is judged by the published declaration without a restart (#26596, #26698):
11+
12+
- **The signal.** The plugin listens to the protocol's post-persistence `onMetadataMutation` notification, which every metadata authoring door emits (save, per-item and package publish, revert, rollback, delete) after the engine registry holds the new definition, and which a cluster peer replays after converging its own registry. Drafts are ignored. It also listens to the `metadata:reloaded` announcement that an artifact reload fires.
13+
- **What it replaces.** The previous subscription probed an optional `watch` member on the metadata service, which `MetadataManager` does not implement, and the runtime authoring path does not announce to the metadata service's watchers in any case.
14+
- **Composition note.** A protocol service that offers no `onMetadataMutation` is reported once at `warn` when the plugin starts.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
'@object-ui/console': patch
4+
---
5+
6+
fix(app-shell): a record page on a deployment without the approvals service no longer logs the record-lock read's 501
7+
8+
Clause-②: no
9+
Metadata-protocol: none
10+
11+
On a deployment that composes no approvals service, such as a fresh `npm create objectstack` scaffold, every record page asks the server whether an approval locks the record, and the server answers `501 NOT_IMPLEMENTED`. That answer is designed: it means there is no lock service, so no approval can lock the record, and the page already treated it that way. But the read went through the SDK client, which logs every non-2xx response as an error before its caller sees it. So each record open printed `HTTP request failed: GET /api/v1/approvals/records/OBJECT/ID/lock -> 501 [NOT_IMPLEMENTED]` in red, which reads like a broken server.
12+
13+
What changes:
14+
15+
- The record page reads the lock directly, the same way it already reads the approval requests, so no client logger runs first. A 501 is read silently.
16+
- After a 501, the page does not ask again for any record until the page is reloaded. The browser's own network line for that first request still appears once, because the server answers the approval-requests read `200` with an empty list and the page cannot tell the service is missing until it asks.
17+
- Any other failure of the lock read still logs, through the same logger the SDK client used: `HTTP request failed: GET URL -> STATUS [CODE]`, with a 404 still demoted to a debug line. The next record open asks again.
18+
19+
What to do: nothing. Nothing a flow, page or object declares changes.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@object-ui/types': minor
4+
'@object-ui/react': minor
5+
'@object-ui/components': minor
6+
'@object-ui/plugin-calendar': minor
7+
'@object-ui/plugin-gantt': minor
8+
'@object-ui/plugin-grid': minor
9+
'@object-ui/plugin-kanban': minor
10+
'@object-ui/plugin-list': minor
11+
'@object-ui/plugin-map': minor
12+
'@object-ui/plugin-timeline': minor
13+
'@object-ui/plugin-tree': minor
14+
'@object-ui/plugin-view': minor
15+
'@object-ui/plugin-detail': minor
16+
'@object-ui/app-shell': minor
17+
---
18+
19+
feat(spec,ui)!: a record opens one way per object — `navigation.mode` keeps `drawer` (now the default), `page`, `new_window` and `none`; `modal`, `split` and `popover` are retired, and a list page's `recordAction` drops `modal`
20+
21+
Clause-②: no (narrowing)
22+
23+
Metadata-protocol: narrowed (`navigation.mode` refuses `modal`, `split` and `popover`; `interfaceConfig.recordAction` refuses `modal`; an omitted `navigation.mode` defaults to `drawer` instead of `page`)
24+
25+
<!-- adr-0087: registered navigation-mode-overlay-values-removed, ui-record-open-overlay-modes-retired -->
26+
27+
**BREAKING** accept-set narrowing and default change on the record-open surface (`@objectstack/spec`), with the console following it in the same release.
28+
29+
The model, ruled by the maintainer: an object has one default way to open a record — the drawer, which expands to the full page (the same record page at two widths) — and `page` only for an object that declares it. A centred dialog and a popover were extra surfaces for the same record, and the side-by-side pane is each reader's own choice, not something an author fixes for every reader.
30+
31+
- **FROM → TO.** `navigation.mode: 'modal'`, `'split'` or `'popover'` (on a list view, an object's `listViews` entry, or a page list block: `list-view`, `object-grid`, `object-kanban`, `object-calendar`, `object-map`, `object-gantt`, `object-tree`, `object-timeline`) → delete the value; the block takes the `drawer` default. `interfaceConfig.recordAction: 'modal'` → delete the value; the default is `drawer`. Declare `page` only for an object whose record should open as a full page.
32+
- **The one-line fix.** Delete the retired value. `os migrate meta --from 17` lists the edit for existing sources, and `--write` applies it. A stored `sys_metadata` row is converted when it is read (the `navigation-mode-overlay-values-removed` conversion), so a view saved from Studio with one of the three values keeps loading and opens the drawer.
33+
- **What the parse says.** Each retired value is refused by name with its prescription, for example "`navigation.mode: 'modal'` was removed in @objectstack/spec 18.3.0 (ADR-0087 D2) — …", at `defineStack` as `STACK_SCHEMA_INVALID` / 422. TypeScript rejects each at a `NavigationMode` position. A misspelt mode keeps zod's own message.
34+
- **The default.** A navigation block written without `mode` parses to `drawer` (it parsed to `page`). The console never honoured that `page` for a list with no navigation block, because it guessed the surface from the object's field count; that guess is retired too. A list of an object with no authored navigation opens a clicked record in the drawer, whatever its field count, and a mobile viewport still pages. An artifact or stored row that a released toolchain parsed carries the old `page` written out and keeps opening as a full page; delete it to take the drawer.
35+
- **The split pane.** It is now a reader's preference, read by the console's object page from the browser (`os:record-open-mode`, `drawer` or `split`, default `drawer`). It applies only to a record that opens in the drawer; an object or view that declares `page` keeps its declaration.
36+
37+
The console (`@object-ui/*`): `useNavigationOverlay` defaults a block without `mode` to `drawer` and treats `drawer` as the one overlay mode. `NavigationOverlay` keeps the drawer face and the split face the console's object page draws from the preference; its `modal` and `popover` faces, the `popoverTrigger` / `popoverAnchorRef` props and the `useOverlayAnchor` export are removed, because no caller can reach them. The grid, kanban, calendar, gantt and tree renderers drop their `split` and `popover` plumbing. The registered `object-view` renderer opens records in the drawer (a page on a mobile viewport) instead of paging a field-heavy object. The Studio list-view inspector offers the four live modes, and the list page's `recordAction` the three live values.
38+
39+
Migration for code that imported the removed component API: drop `popoverAnchorRef` / `popoverTrigger` from a `NavigationOverlay` call and any `useOverlayAnchor()` call; pass `mode="split"` with `mainContent` for a side-by-side pane.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@object-ui/i18n': patch
3+
---
4+
5+
fix(app-shell,i18n): the Studio hook editor grants a sandboxed-JS body its capabilities
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
A hook body that calls `ctx.api`, `ctx.log` or `ctx.crypto` needs the matching token in `body.capabilities`, or the sandbox throws when the hook runs. The Studio hook editor had no control for that list, so a hook that read data could only be fixed through the metadata API.
11+
12+
- **Studio (Data › Hooks, and the metadata admin's hook editor).** A sandboxed-JS body now shows a "Capabilities" checkbox per `HookBodyCapability` token (`api.read`, `api.write`, `api.transaction`, `crypto.uuid`, `log`), each with the `ctx` call it unlocks. Ticking one writes `body.capabilities`; unticking the last writes `[]`, the spec's default. An expression body shows no control, and switching a body to an expression drops its grant and its `timeoutMs` / `memoryMb` limits, since an expression body refuses those keys. A stored token the spec does not list stays on screen, flagged, until it is unticked. Nothing is inferred from the body's source.
13+
- **`@object-ui/i18n`.** The `studio` namespace gains `engine.inspector.hook.capabilities`, `engine.inspector.hook.capabilitiesHint` and `engine.inspector.hook.capabilityUnknown` in `en` and `zh`.
14+
15+
No metadata key is added or changed, and the sandbox's capability check is as strict as before. What to do: nothing.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@object-ui/i18n': patch
3+
---
4+
5+
feat(i18n): the `studio` namespace words a refused publish, and a refusal on a dashboard widget or a report input
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Rows for objectstack#26730.** Studio keeps a refused publish on its Pending changes entry until it is fixed, and its "Show me" now ends on the input a refusal names. The namespace gains the sentences for that: a publish refusal (`engine.studio.refusal.publishIssue`, `publishWhere`, `publishPlain`, `publishUnlocated`), the toast that points at the sheet (`engine.studio.publishRefused`, `publishRefusedOne`, `publishRefusedBatch`), and where a dashboard widget or a report's own input is named (`engine.studio.refusal.widget`, `widgetInput`, `reportInput`). Each row has its `zh` text. No existing row changes, and no export changes.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@object-ui/i18n': patch
3+
---
4+
5+
feat(i18n): the `studio` namespace gains `engine.studio.rail.clearSearch` (en "Clear search", zh "清除搜索")
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The Studio rails (Data, Automations, Access) now share one empty-search state: a search that matches nothing names the query under the box and offers a "Clear search" action that empties the box and puts every row back. This row is that action's label; the sentence reuses the existing `engine.list.emptyQuery` row.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@object-ui/i18n': patch
3+
---
4+
5+
fix(app-shell): the dashboard's Add-widget menu and the widget Type select offer every widget type the spec accepts, from one registry
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
Studio's two dashboard widget-type pickers kept their own lists and disagreed with each other and with the spec: the Add-widget menu offered 11 types, the inspector's Type select 10 (no `scatter`), and `ChartTypeSchema` accepts 20, all of which a dashboard draws. Both pickers now read one registry derived from `ChartTypeSchema`, so they offer the same 20 types and follow the spec when it changes.
11+
12+
- The `studio` namespace gains the names of the newly offered types: `engine.inspector.widget.type.*` for `column`, `scatter`, `treemap`, `sankey`, `combo`, `gauge`, `solidGauge`, `kpi`, `bullet` and `radar`, and `engine.widgetPicker.type.*` for the same types except `scatter`, in en and zh.
13+
- `engine.inspector.widget.datasetHint` no longer mentions an "inline object query above", which the widget inspector has not had since ADR-0021; it describes the dataset, dimension and measure controls the inspector shows.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/skills': patch
3+
---
4+
5+
docs(skills): the i18n skill's group table names `fields.{field}.inlineTitle` and `reports.{report}.blocks.{block}.{label,description}`
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
`skills/objectstack-i18n/SKILL.md` did not name two keys the 18.2 translation bundle already accepts and the runtime already serves, so an AI author writing a bundle from the skill left a master-detail field's inline child-grid heading and a joined report's block headings in the authoring language. The `fields.{field_name}` row now names `inlineTitle` (addressed on the child object that owns the field; untranslated it keeps its authored text, with no fallback to the child's plural label), the `reports` sentence names `reports.{report}.blocks.{block_name}.{label,description}` (keyed by the block's required `name`), and one example row each, spelled as the shipped `app-crm` and `app-showcase` bundles spell them, follows the group table. The eval file gains one prompt covering both keys.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/skills': patch
3+
---
4+
5+
docs(skills): the objectstack-platform skill teaches the `demoStaff` `manager` key — another entry's email, written as `manager_id` by the development boot (#26764)
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The Demo accounts section of `objectstack-platform/SKILL.md` now names a line manager in its `demoStaff` example and says how the key behaves: `manager` is another entry's `email`; `os dev` writes `sys_user.manager_id` itself once every demo account exists and keeps a manager someone set by hand, so no seed hook writes it; a display name, an address no entry has, the entry itself or a loop is refused at load. One eval case in `evals/config-plugins-ops.json` expects the key and forbids a seed writing `manager_id`. Three restatements elsewhere in the skill were cut against the addition, each rule keeping its other home in the file; the remainder raises the file's token ceiling by the measurement.

0 commit comments

Comments
 (0)