Skip to content

Commit 5f06db0

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/project-thread-kkdbga
2 parents e92495c + 5780fa4 commit 5f06db0

121 files changed

Lines changed: 8113 additions & 631 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: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/skills': patch
3+
---
4+
5+
docs(skills): the ADR-0126 §1.3 promise pages and the skills teach what ADR-0146 D2 delivers — customizations in `.custom`, `__c` fields, the flow override and "new similar", no package-less overlay
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Before.** The skills taught the pre-epic paths: `objectstack-data` named no home for a customer's extension of a shipped object and no `__c` rule, and its lock table still called an edit of a shipped item an "overlay"; `objectstack-automation` said nothing about a flow an installed package ships, so an agent cloned it (two copies running) or edited it in place (refused); `objectstack-platform` taught the declared override's read but not who may declare it, how the pair is created, or what the three non-overridable types do. The docs promise pages (`capabilities/integrations`, `build-without-code`) still said views and dashboards are "yours to edit directly" and sent readers to a Setup packaged-automation page to switch off and clone a flow.
11+
- **Now.** `objectstack-data` says an object an installed package ships is extended from the base's paired `.custom` package, every added field ends in `__c`, the suffix is refused anywhere else and an unsuffixed field is refused in the pair, a base field changes only through `fieldPatches`, and Studio's Add field writes the same extension; its lock table reads "Edit", not "Edit (overlay)". `objectstack-automation` says a packaged flow is customized by a same-named declared override in the pair (armed in the base's place, one activation switch, a suspended run finishes on the body it started on), that an undeclared same name is refused at publish, and that the clone door is "new similar": a copy under a new name, switched off, in the pair, never package-less. `objectstack-platform` adds the ensure door and its two manifest facts, the customizations read, the uninstall refusal, the write gate's one rule for dependents (rename), and "new similar" for permission sets, positions and capabilities. One eval per skill asserts the taught shape and refuses the old one.
12+
- Every file stays under its token ceiling: the new text is paid for by deleting restatement in the same file (a layout tree the Template section and the Naming table already state; a search-mirror maintenance table and a formula aside restating the sentence above them; the dates and money legacy → CEL rows `objectstack-formula`'s table holds; the CEL callout restating that skill's pointer; two `runAs` notes and a `readonly` note tightened, no rule removed). The touched ceilings lower to their new measurements; no ceiling rises, no frontmatter, schema or behaviour changes. The docs site's promise pages, install modes, object extensions, metadata lifecycle, permission sets, CLI and metadata API pages gain the same facts.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@objectstack/runtime': patch
3+
---
4+
5+
fix(runtime): an MCP grant that holds no federation capability is no longer listed `list_remote_tables` / `import_object` (#28938)
6+
7+
Clause-②: no — no call that passed before is refused now, and none that was refused passes: what changes is that a request whose every federation call would answer `403 PERMISSION_DENIED` is no longer listed the pair.
8+
Metadata-protocol: none
9+
10+
The `/mcp` door hands an OAuth agent the delegating user's system capabilities only when the grant names `actions:execute`. A `metadata:write`-only grant therefore holds none, yet it was listed `list_remote_tables` and `import_object`, and every call answered `403 PERMISSION_DENIED`, even when the user is an administrator.
11+
12+
- **Now.** The runtime's MCP bridge carries the pair only when the request holds `manage_platform_settings` (the listing's capability) or `manage_metadata` (the import's). Without either, `tools/list` omits both, the way `list_actions` omits an action its caller may not run. The listing decision and the per-call check read the same capabilities, so they cannot disagree.
13+
- **Unchanged.** A grant naming `metadata:write` and `actions:execute`, for a user who holds the capabilities, lists and calls both as before. A request holding one capability is still listed both, and the call that needs the other still answers `403`. API keys and sessions are still not listed the pair.
14+
- **To use the pair from an MCP client:** connect with `metadata:write` and `actions:execute`, as a user who holds the capability.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@object-ui/plugin-list': patch
3+
'@object-ui/app-shell': patch
4+
'@object-ui/i18n': patch
5+
---
6+
7+
feat(plugin-list, app-shell): one "Personalized" marker beside the list title holds the one reset to the view
8+
9+
Clause-②: yes (widening)
10+
Metadata-protocol: none
11+
12+
- **The marker.** While a reader's list differs from its view, the Console draws a **Personalized** marker beside the list title (in the page header, and beside the phone title as an icon alone). Opening it names what differs (filters, search, sort, grouping, column layout, hidden fields, density, layout, row color, row numbers, inline editing) and holds **Reset to view**, with a sentence saying what it covers and what it leaves alone. It is not configurable: no metadata key or option.
13+
- **The one reset.** It drops the active view's stored entry and the cached search term, removes the reader's whole `uf_*` family from the address bar (Filter panel, search, sort, grouping and every user-filter chip), and opens the list again, so a reload or Back does not bring the old list back. Not list state, so untouched: the record-open preference, pinned views and their order, favorites, and a "View all" link's `filter[...]` scope.
14+
- **Gone.** The header ⋯ entry "Reset view", and, on the Console list, the Display panel's "Reset to view default". The sort popover's reset and the Filter panel's "Clear all" stay: each puts back one control.
15+
- **`ListView` (`@object-ui/plugin-list`)** gains one optional host prop, `onPersonalizationChange(parts)`, reporting which parts of the list on screen differ from the view (`sort`, `filters`, `search`, `grouping`, `hiddenFields`, `density`, `layout`, `rowColor`, `rowNumbers`, `inlineEdit`; `columns` is in the part type for a host to add). With it the Display panel draws no reset of its own; without it nothing changes. `viewDefaults` also accepts `grouping`, the view's own grouping under one the host laid over it; a `viewDefaults` without that key leaves the grouping `schema` opened with as the view's.
16+
- **Copy (`@object-ui/i18n`).** New `console.objectView.personalized`, `personalizedHeading` and `personalizedParts.*` in all ten packs; `console.objectView.resetView` and `resetViewTitle` now label the marker's reset and say everything it covers. No key is removed.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
fix(spec)!: a dashboard that declares two global filters under one name is refused at every door that parses it
6+
7+
Clause-②: no (narrowing)
8+
Metadata-protocol: narrowed
9+
Skills-docs: none — `skills/objectstack-ui/rules/dashboards.md` teaches a filter's `name` as "the stable key `filterBindings` references", defaulting to `field`, and no file under `skills/**` shows two filters under one key; every taught use stays true.
10+
11+
**BREAKING** for a dashboard whose `globalFilters` repeats a filter key.
12+
13+
- **Before.** `DashboardSchema` typed `globalFilters` as a plain array, so two entries under one key parsed and the dashboard save door (`PUT /api/v1/meta/dashboard/X`) answered 200. A filter's key is its `name`, or its `field` when it has no `name`: the key the filter bar draws its control under and the key a widget's `filterBindings` names. The board drew one control, and the value typed into it filtered the field of the other definition, so a tile read a number the author never asked for.
14+
- **Now.** `DashboardSchema.globalFilters` refuses the pair, so every door that parses a dashboard refuses it: `defineStack`, `os validate`, `os build` and the dashboard save door (422 `INVALID_METADATA`). One issue per repeated key, at the second declaration's `name` (or its `field`, when that entry has no `name`), naming the key and every index: `This dashboard declares the global filter 'X' twice in globalFilters: globalFilters[i] and globalFilters[j]. …`. The rule sits on the array, so a mirror that spreads `DashboardSchema.shape` keeps it.
15+
- **Not changed.** Distinct names, two filters with distinct names over the same `field`, and the same name on two different dashboards.
16+
- **Fix.** Rename all but one of the colliding entries (and every widget `filterBindings` key that means a renamed one), or delete the copy.
17+
- **Stored rows.** A stored dashboard that repeats a key still loads, with `_diagnostics` reading `valid: false` and carrying the refusal, and its next save (draft or publish) is refused until it is fixed. Nothing rewrites it: which definition keeps the name only the author knows. The upgrade guide entry is `ui-dashboard-global-filter-duplicate-name-refused`.
18+
19+
<!-- adr-0087: registered ui-dashboard-global-filter-duplicate-name-refused -->
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 record-page rule names drawer and full page as the reader's only Open records choices
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The console retired the split pane beside the list: a list record opens in the drawer (the default) or as the full page, and the Display menu's Open records offers those two only. `skills/objectstack-ui/rules/record-page.md` rule 6 still taught "split beside the list — the default — or drawer, or full page", so an agent reading it told authors that records open beside the list and offered a choice that no longer exists. Rule 6 now names the two choices and says the split beside the list is retired; the drawer's dragged width and ⤢ stay reader preferences. Eval 2's expected answer says the same: a pane beside the list is no longer offered, nothing is authored for it, and the contract object keeps `navigation: { mode: 'page' }`.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
---
4+
5+
fix(studio): an Interfaces edit whose save the server refused counts as unsaved, so leaving or reloading asks first
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
When the server refused a report's, dashboard's or page's autosave (a 422, say), the edit stayed on screen but Studio's leave guards did not see it: a reload, a pillar or Home link, or opening another item in the Interfaces rail dropped it with no question. The Interfaces pillar now reports such an edit as unsaved work beside its navigation edits, so the browser's "leave site?" prompt, the Studio header and the shell's link guard ask, and the rail and the New dialog ask before another item replaces it. The same holds for an object item's list-view edit whose save was refused. An edit whose save lands, or the load of another item, clears it; an item you have not edited never asks.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@object-ui/components': patch
3+
---
4+
5+
feat(components): the filter panel offers relative dates on date rows, stored as date-macro tokens
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
A `date` or `datetime` row of the filter builder (the list filter panel, and the other surfaces that share the builder) now offers relative dates beside a fixed date:
11+
12+
- **`between`** offers the 13 date-range presets of `@objectstack/spec` (`DATE_RANGE_PRESETS`, in that order: today, yesterday, this week, … last 90 days) beside `Custom…`, which keeps the two date inputs. A chosen preset is stored as the spec's macro window from `DATE_RANGE_PRESET_MACRO_WINDOWS`, for example `['{week_start}', '{week_end}']`.
13+
- **`before` / `after`** offer today, yesterday and tomorrow beside `Custom…`. A chosen point is stored as the wrapped token, for example `'{today}'`.
14+
15+
The stored rule stays an ordinary `ViewFilterRule`: no operator, key or value kind is added, a preset name is never stored, and the query resolves the tokens when it runs, so a filter saved as "this week" still means this week next Monday. A stored pair reads back as its preset (the `{current_*}` aliases fold first: `['{current_week_start}', '{current_week_end}']` shows "This week"); a placeholder value no preset matches, such as `['{7_days_ago}', '{tomorrow}']`, is kept, shown as its text, and still filters. A stored fixed date shows as `Custom…` with its date, as before, and still means that day.
16+
17+
The labels reuse the keys the dashboard filter bar and the date picker already translate (`dashboard.filters.range.*`, `dashboard.filters.custom`, `fields.datePicker.tomorrow`). Relative values on `equals` are not offered.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
'create-objectstack': patch
3+
---
4+
5+
fix(create-objectstack)!: a scaffold into the current directory refuses, writing nothing, when a template file would overwrite one already there (#29134)
6+
7+
Clause-②: no (narrowing) — `create-objectstack` with no name (or an empty one) in a current directory holding a file the template writes (`README.md`, `package.json`, …) refuses with exit 1 and leaves the directory untouched, where it overwrote those files and exited 0 before. Nothing is widened.
8+
Metadata-protocol: none
9+
10+
<!-- adr-0087: not-required (no-migration-prescription) No key of any schema, no export of a published entry and no stored shape changes: the scaffolder's command line refuses one more case before it writes, so there is nothing for a migration to rewrite and no tombstone. -->
11+
12+
**BREAKING** for a script that runs `create-objectstack` with no name inside a directory that already holds files the template writes.
13+
14+
- **Before.** A named directory that was not empty was refused (`Directory already exists and is not empty`, exit 1), but the current directory was not checked at all: the template was copied over whatever was there, so an existing project's `README.md` and `package.json` were replaced without a word and the run exited 0.
15+
- **Now.** Before anything is written, the run compares the template's files with what the current directory holds. If any template file would replace an existing file (or a file stands where the template needs a directory), it refuses with exit 1, names each of those paths, and writes nothing.
16+
- **Unchanged.** A current directory that holds only files the template does not write (a `.git/`, a notes file) still scaffolds, and those files are left as they were; the closing summary still says the directory already had contents. An empty current directory and a named directory behave as before.
17+
18+
**The remedy.** Move the named files aside and re-run, or scaffold into a new directory: `create-objectstack my-app`.
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): the list view inspector, the email template and translation form editor, the permission matrix and the hooks panel no longer edit the published version over a draft the author is not allowed to read
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Before.** A pending draft can carry an audience gate (`requiredPermissions`) its author does not hold, for example one written over the API or MCP, and the draft read answers `403 PERMISSION_DENIED` for it by design. `readDraftForSave` hands that refusal back as `refused`, but these four editors still read it as "no pending draft": they opened the published body in its place, and their next save sent that body as the draft, dropping the gate and every other unpublished change.
11+
- **Now.** Each of them installs no buffer for such an item: nothing is shown to edit and the autosave has nothing to send. The refusal strip says the item has an unpublished draft this author may not read and that editing the published version would overwrite it; the server's own sentence is behind Details. The hooks panel holds only the refused hook: the other hooks of the object open and save as before.
12+
- **Unchanged.** An item with no pending draft opens the published body and its first save is still pinned to "no draft row"; a readable draft opens as served. Any other failed draft read is handled as before.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
fix(spec): a field's translated help reaches its authored `inlineHelpText`, the line the console draws
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
Nothing changes in what a metadata author can write. What changes is the served, translated read of an object: `translateObject` (the resolver behind `GET /api/v1/meta/object/:name` and every other localized object read) now serves a field's `objects.OBJECT.fields.FIELD.help` translation on the field's own `inlineHelpText` when the field authors one.
11+
12+
- **Before.** The translation was served on `description` only. The i18n extractor scaffolds that entry from `help ?? inlineHelpText ?? description`, and the console draws a field's help line from `inlineHelpText || description`, so a field that authored `inlineHelpText` showed its source-language text in every locale and its translation never appeared.
13+
- **Now.** A field that authors a non-empty `inlineHelpText` is served the translation there, and on `description` as before. A field without one (absent, or an empty string, which the console reads as absent) gains no `inlineHelpText` key; its translation stays on `description`, which the console already draws.
14+
- **Unchanged.** A locale with no `help` entry for the field, and the source locale, serve the authored text. Each slot keeps a value that diverged from the packaged declaration (a tenant's own edit), judged per slot by the same rule that already guards `description`. Nothing is ever served on a `help` key.

0 commit comments

Comments
 (0)