Skip to content

Commit 523d1d3

Browse files
committed
Merge origin/main into claude/project-thread-sfzn7w (ratchet rows beside #26446)
PR #26448 went dirty after #26446 (d1a31d0) raised the objectstack-automation rows of scripts/check-skills-token-ratchet.mjs beside the objectstack-platform rows this branch re-pins. Resolved by keeping both sides: main's sibling rows and this card's rows; nothing else conflicted. check:skills-token-ratchet exit 0, --self-test exit 0 (79 cases). Claude-Session: https://claude.ai/code/session_01JQwxbX1XjSzpntF9n85TgB Co-authored-by: Claude <noreply@anthropic.com>
2 parents 198e492 + c91544a commit 523d1d3

104 files changed

Lines changed: 3950 additions & 646 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+
'@objectstack/spec': patch
3+
---
4+
5+
docs(spec): `onSuccess.navigate` and a `type: 'url'` action's `target` say that an in-app path is an app-qualified console route (`/apps/APP/OBJECT/record/ID`) (#25458)
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
Wording only; no schema accepts or refuses anything new. The describe text of `ActionSchema.onSuccess.navigate` and of `ActionSchema.target` said "an in-app path starting with / or ./" and never said which path. They now say that an in-app path is a console route, written without the console's mount prefix, and that a record is `/apps/APP/OBJECT/record/ID`. `APP` is the app's route segment (ADR-0048): its package id, and its `name` also resolves. No app-relative form is declared and no `${ctx.*}` member names the current app, so write the app segment. The docblock example `/apps/crm/tasks/${result.id}` becomes `/apps/crm/tasks/record/${result.id}`, the route it was only redirected to.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
feat(spec)!: a `runAs: 'system'` hook or a `script` action whose sandboxed body writes an identity-assignment object is refused at save
6+
7+
App-authored automation does not use elevation to write `sys_user_position`, `sys_user_permission_set`, `sys_invitation` or `sys_member`. The data middleware already refuses that write at run time with `PERMISSION_DENIED` (403), from a system flow run, a hook that runs as system and an action body. Until now every authoring door still accepted a hook or action whose body says so in writing, and the author learned otherwise only from a failed write. The parse now refuses it too:
8+
9+
- `HookSchema` refuses a hook that declares `runAs: 'system'` and carries a sandboxed JS body (`language: 'js'`) whose `source` calls `insert`, `update`, `delete`, `updateMany`, `deleteMany` or `upsert` on `ctx.api.object('<object>')` for one of the four objects, on the handle or on a name the body bound it to.
10+
- `ActionSchema` refuses a `type: 'script'` action whose sandboxed JS body does the same. An action body always runs as system.
11+
- One `custom` issue per object, at `body.source`, naming the object and the route: `runAs: 'user'` (a hook) or a `runAs: 'user'` flow (an action) for position holdings and permission-set grants, so the ADR-0090 D12 delegated-administration gate judges the user; the organization endpoints for invitations and memberships. It surfaces at `os validate`, at `defineStack` (`422 STACK_SCHEMA_INVALID`), at an artifact's parse and at the metadata save door (`422 INVALID_METADATA`).
12+
- `Hook.runAs` now says that elevation does not reach identity assignment, as `FlowSchema.runAs` does.
13+
14+
Unchanged: reads of the four objects, writes to every other object, a hook under `runAs: 'user'` or `'inherit'`, an expression (L1) body, a code `handler`, and the run-time refusal, which stays the enforcement for every elevated write.
15+
16+
Clause-②: no (narrowing) — `HookSchema` and `ActionSchema` refuse a body they used to accept. Nothing is widened.
17+
18+
Metadata-protocol: narrowed (a `runAs: 'system'` hook or a `script` action may no longer carry a sandboxed body that writes an identity-assignment object)
19+
20+
Ruling-ref: 6384657105 (letter A); retriage 6431012701
21+
22+
<!-- adr-0087: registered hook-action-body-system-identity-assignment-write-refused -->
23+
24+
**BREAKING** for an author whose system hook or action body writes one of the four objects. Such a write was already refused when the body ran.
25+
26+
- **Before.** The hook or action saved, and its write failed at run time with `PERMISSION_DENIED`.
27+
- **Now.** The hook or action is refused at save, at `body.source`.
28+
- **Fix.** For a position holding or permission-set grant, set the hook's `runAs` to `'user'`, or move an action's grant to a flow whose `runAs` is `'user'` (an action of `type: 'flow'` runs it as the invoking user). For an invitation or membership, call `POST /api/v1/auth/organization/invite-member`, `cancel-invitation`, `update-member-role` or `remove-member`.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@object-ui/plugin-detail': patch
3+
---
4+
5+
fix(plugin-detail): empty related lists fold into one line after the lists with records; `record:path` terminal stages share the rail's width
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Related tab.** Stacked `record:related_list` blocks whose lists have no records no longer take a full row each wherever they were declared. Once every list of the stack has answered, the empty ones fold into one line after the last list, naming each one and keeping its **New** action. A list that gains a record returns to its place. A stack whose lists are all empty, a list on its own, and an empty list that offers more than New (an `add` picker, child toolbar actions) draw exactly as before.
11+
- **Path bar.** On the desktop rail every stage now takes an equal share of the width, terminal stages included, and a terminal stage's label wraps instead of being cut. Before, the separated lost group was only as wide as its words, so a short path drew its terminal stages in one narrow cell at the right edge. The mobile row is unchanged.
12+
- **`RelatedList`** takes an optional `onEmptyChange(empty)` callback, told whether the list is empty once its read answers (`null` while a read is in flight). Nothing else about the component changes.
13+
- No key is added to, removed from or reinterpreted on any block. Nothing to do.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
---
4+
5+
refactor(app-shell): the Console `ObjectView`'s page header and record overlay move into sibling sub-components
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
Two blocks of `ObjectViewInner`'s JSX in `views/ObjectView.tsx` move verbatim into sub-components. The page header (title, managed-by badge, icon, create button, Import, the toolbar actions, the overflow menu and the folded face) moves into `ObjectViewPageHeader` in `objectViewPageHeader.tsx`. The record detail overlay drawn for every navigation mode but `split` moves into `ObjectViewRecordOverlay` in `objectViewRecordOverlay.tsx`. The component passes every value in as a prop. No logic, name or rendered output changes, and the package entry is unchanged.
11+
12+
What to do: nothing.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@object-ui/plugin-grid': patch
3+
---
4+
5+
refactor(plugin-grid): `ObjectGrid`'s authored-input diagnostics move into a sibling hook
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The three `console.warn` effects that report an unreadable `columns` entry, an unusable `bulkActionDefs` member and a non-positive page size move verbatim out of `ObjectGrid.tsx` into `useObjectGridDiagnostics` (`objectGridDiagnostics.ts`). `ObjectGrid` calls the hook where the effects stood, so its hook order, its warnings and its rendered output do not change. No name is renamed, and the package entry is unchanged.
11+
12+
What to do: nothing.

‎.changeset/26095-publishing-metadata.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@
7171
'@objectstack/trigger-schedule': patch
7272
'@objectstack/types': patch
7373
'@objectstack/verify': patch
74-
'create-objectstack': patch---
74+
'create-objectstack': patch
75+
---
7576

7677
chore(packages): publishing metadata — per-format types in `exports`, declared `sideEffects`, and a README for the 18 published packages without one
7778

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
'@object-ui/plugin-map': patch
3+
'@object-ui/react': patch
4+
'@object-ui/app-shell': patch
5+
'@object-ui/i18n': patch
6+
---
7+
8+
feat(plugin-map): a map view with no style of its own draws the deployment's default map style before the public demo style
9+
10+
Clause-②: no
11+
Metadata-protocol: none
12+
13+
An operator who sets `OS_CONSOLE_MAP_STYLE_URL` now gets that style on every map view that names none. A map draws the first style it finds, in this order:
14+
15+
1. the view's own style: `mapStyle`, then `map.style`;
16+
2. the deployment's default style, the runtime-config `mapStyleUrl`;
17+
3. the public MapLibre demo style.
18+
19+
- **`@object-ui/react`:** `HostMapStyleProvider` and `useHostMapStyle()` let a host hand the deployment's default map style to the map renderer. Outside a provider, the hook answers `undefined` and the map keeps the demo style.
20+
- **`@object-ui/app-shell`:** the runtime config reads the top-level `mapStyleUrl` into `AppShellRuntimeConfig.mapStyleUrl`, re-read from each payload, and `ConsoleShell` supplies it through `HostMapStyleProvider`.
21+
- **`@object-ui/plugin-map`:** `ObjectMap` reads the host's style after the view's own. When the deployment's style fails to load, the notice above the record list says the deployment's default map style could not be loaded and asks the reader to have an administrator check it. It no longer blames the public default tiles, which that map never drew.
22+
- **`@object-ui/i18n`:** one new key, `element.map.styleFailedDeployment`, in all ten packs.
23+
24+
What to do: nothing. A deployment that sets no `OS_CONSOLE_MAP_STYLE_URL` draws the demo style as before, and a view's own `mapStyle` still wins.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
spec(ui): a navigation group that says nothing opens — `GroupNavItem.expanded` defaults to `true`
6+
7+
Clause-②: yes
8+
Metadata-protocol: semantic
9+
10+
<!-- adr-0087: registered ui-nav-group-expanded-default-open -->
11+
12+
**BREAKING** — a declared default moves, shipped as `minor` under the launch-window convention. The author who relied on the documented default (`false`, "a group starts collapsed unless the author opens it") for a group meant to stay out of the daily user's sight — an advanced, developer or diagnostics section — now sees it open on first view wherever the app is parsed (`defineStack`, `defineApp`, a compiled `objectstack.json`). The remedy is one key on that group: `expanded: false`.
13+
14+
- **Before.** `GroupNavItemSchema.expanded` defaulted to `false`. An app parsed through `defineStack` or `defineApp` (and so every compiled `objectstack.json`) carried a written `expanded: false` on every group whose author said nothing. The console honours a written `false` as an explicit close, so those groups started collapsed. An app registered without a parse (the platform's Setup and Account apps) arrived without the key, and the console opened its groups. The protocol and the console disagreed about what a silent group means.
15+
- **Now.** The default is `true`. A group that says nothing parses to `expanded: true` and opens on first view, the same answer the console already gave a group without the key. A group holding the current route still opens whatever it declares.
16+
- **To keep a group closed**, write `expanded: false`. Setup's Advanced group and Account's Developer group already do, and they still collapse. Every authored `expanded` parses exactly as before, and a non-boolean is still refused.
17+
- **Compiled artifacts.** A document that serialised an earlier parse (an `os compile` artifact) carries the old written `false`, and its groups stay closed. Recompile it, or delete the key.
18+
- **Upgrade path.** The ADR-0087 D3 semantic entry `ui-nav-group-expanded-default-open` carries the change to `os migrate meta` and the upgrade guide. The default change is declared in the build's default-change ledger. No key is added or removed.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
---
4+
5+
fix(app-shell): on a narrow window, a toast raised beside the record drawer no longer covers the record's Edit and ⋯ buttons
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
When a right-edge drawer is open and the window is too narrow for a toast beside it (a 720px record drawer on a 1024px window, a split screen, a phone), the console's toaster drops below the drawer's header. It cleared only the drawer's own header, so the success toast raised by creating a record sat on the record header's Edit button and ⋯ menu for its whole four seconds. The toast now also drops below the drawer's first row of controls, measured from the drawer's layout, so it clears the record's actions row for any drawer content. Wide windows keep the toast in the strip beside the drawer, as before.
11+
12+
What to do: nothing.

‎.github/workflows/half-state-patrol.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -356,8 +356,8 @@ jobs:
356356
# The judge's own cases first, and the sweep only if they hold: this
357357
# step WRITES to other people's cards, and a broken predicate that
358358
# still runs is the one failure mode that cannot be undone by the next
359-
# run. `check:pm-closed-card-sweep` is the same command under a dev
360-
# -facing name; this is the invocation CI holds.
359+
# run. It has no root alias: this job sets up no pnpm, so this line is
360+
# the one definition (the GATE INVOCATION IDIOM note in lint.yml).
361361
set +e
362362
node scripts/pm/sweep-closed-cards.mjs --self-test > "$RUNNER_TEMP/closed-cards-selftest.log" 2>&1
363363
selftest=$?

0 commit comments

Comments
 (0)