Repository navigation
Commit 7afdc5c
fix(rest): the /meta dashboard and view reads hand the translator the packaged base, so a published org overlay beats the packaged catalog (#20832)
Fixes #20730
Clause-②: no
## What this changes
An org's published edit to a packaged dashboard or view is now what the
`/meta` item and list reads serve, in every locale. Before, the reads
served the bundle's translation of the string the package shipped.
`packagedObjectBaseOf` in `packages/rest/src/meta-item-read-gate.ts` is
now the one per-type packaged-base resolver that the triage asked for.
It reads one table, `PACKAGED_BASE_ACCESSORS`:
| type | protocol accessor |
|:--|:--|
| `object` | `getPackagedObjectBase(name)` (unchanged) |
| `dashboard` | `getPackagedDashboardBase(name)` |
| `view` | `getPackagedViewBase(name)`, by the served item's qualified
`OBJECT.VIEW_KEY` registry name |
`translateMetaDocument` (the item read) and `translateMetaList` (the
list read) now resolve the protocol for every type in that table instead
of for `object` only. Both `/meta` transports (the REST server's routes
and the runtime's HTTP dispatcher) call these two functions. There is no
second resolver; a future type with a packaged-base accessor adds its
row to the table. The translation rule itself (ADR-0029 D9.2a, an
explicit override beats a packaged default) is unchanged in
`@objectstack/spec/system`. This PR only hands the translator the base
it could not see.
`packages/rest/src/rest-server.ts` is not touched: it delegates to these
functions and already hands them its protocol.
`packages/metadata-protocol` and `packages/spec` are read, not edited.
## Pins
`packages/rest/src/meta-dashboard-view-i18n-explicit-override.test.ts`
(52 tests) runs over the REAL `ObjectStackProtocolImplementation` and a
REAL `SchemaRegistry`, through the `/meta` routes. The packaged items
are registered the way the boot registers them, and the org overlay rows
are seeded the way a published overlay stores them.
- §1: the resolver asks each type for its own accessor. It asks nothing
for `action`, `app`, `dataset`, `page`, `report`, `toString` or
`constructor`. It answers `undefined` for an empty name, a missing
accessor or a throwing accessor.
- §2 dashboard: for the item read and the list read, for an admin and a
member of the same org, in `en` and `zh-CN`:
- after an overlay and a publish, the edited widget serves the edit;
- the unedited widget stays translated;
- after a reset, the shipped title is served, translated.
- Control: a packaged dashboard whose catalog has no widget titles
serves an overlay edit exactly as before.
- §3 view: the same three cells for `showcase_task.in_progress`. The
unedited view is `showcase_task.urgent`.
One line in `scripts/engine-double-contract.pinned.json`, written by
`check-engine-double-contract --write` for the new `findOne` double. The
double calls `assertEngineFindOnePredicate`.
## Ablations (one-shot, at `87f1867ea`, source-resolved)
Each ablation went through `scripts/ablation-replace.mjs`. The anchor
hit once in every case, and each restore was proven by the blob hash
matching HEAD and by an empty `git diff HEAD`.
| mutation | result |
|:--|:--|
| resolver answers `object` only (blob `38a35644` to `4aeefa5c`) | 17
failed / 46 passed of 63. The failures are all 16 edit cells plus the §1
dispatch case. The unedited, reset and control cells and the object
suite stay green. |
| list read resolves the protocol for `object` only | 8 failed / 44
passed of 52. The failures are exactly the 8 list-read edit cells. |
| item read resolves the protocol for `object` only | 8 failed / 44
passed of 52. The failures are exactly the 8 item-read edit cells. |
## Measured in a real boot
Scratch probe, not committed: `bootStack(showcaseStack, { orgContext:
true })`, the seeded admin plus a signed-up member of the same org, on
tree `e830f24cb`. The probe ran three phases: pristine; overlaid (admin
`PUT ?mode=draft` then `POST publish`, each answering 200, on
`system_overview`, on the showcase control `showcase_ops_dashboard` and
on `showcase_task.in_progress`); and reset (admin `DELETE`, answering
200). In every phase it read `/meta/dashboard/NAME`, `/meta/view/NAME`,
`/meta/dashboard`, `/meta/view`, `/meta/app` and `/meta/object` for both
callers in `en` and `zh-CN`.
Before (the rest dist rebuilt from the ablated resolver;
`ablation-dist-preflight` found the marker in 2 built files) compared
with after (HEAD): **12 served leaf fields differ, all in the overlaid
phase**:
- `system_overview` `widget_total_users.title` on the item and list
reads, admin and member, `en` and `zh-CN` (8). It went from `Total
Users` / `用户总数` to `Total Users (edited-20730)`.
- `showcase_task.in_progress` `label` on the item and list reads, admin
and member, `zh-CN` (4). It went from `进行中` to `In Progress
(edited-20730)`. An `en` reader was already served the edit before this
change.
- 0 diffs in the pristine and reset phases, on the control dashboard, on
every other dashboard and view, and on the app and object lists.
The restore leg rebuilt rest, and `ablation-dist-preflight --absent`
found the marker absent from all 6 built files. The card cites 16
changed fields from the parent card's probe. That probe covered a
different set of reads, so the two counts are not comparable. Here the
dashboard alone accounts for 8.
## The rendered board, measured in a browser
The console was built from objectui `db11afd4967c` (this repo's
`.objectui-sha`) with `scripts/build-console.sh` and served by `pnpm dev
-- --fresh` at `8ee4569fa`. The browser was headless Chromium from
`/opt/pw-browsers/chromium`, signed in through the console's form as the
seeded admin, with the browser locale set to `en` and to `zh-CN`.
- After the overlay was published, the console's own
`/api/v1/meta/dashboard` responses carried `widget_total_users.title` =
`Total Users (edited-20730)` in both locales, and its
`/api/v1/meta/view` responses carried `label` = `In Progress
(edited-20730)`.
- The drawn board at `/_console/apps/setup/dashboard/system_overview`
still showed `Total Users` (`en`) and `用户总数` (`zh-CN`).
- The view at
`/_console/apps/showcase_app/showcase_task/view/in_progress`:
- `en`: the tab showed the edit, and the breadcrumb showed `In
Progress`;
- `zh-CN`: both the tab and the breadcrumb showed `进行中`.
- After the reset, both were served and drawn as shipped.
So the server half is fixed, and the console still re-resolves these
strings against the bundle in the browser. That half is objectui's. The
measurement is handed to the seat to file, as the triage directed.
## Docs and changeset
- `content/docs/ui/translations.mdx` gains "An edit beats the packaged
catalog", which states the dashboard rule and the view rule side by side
(which strings are compared, and how each is matched), the three
consequences, and the object rule by reference.
- `.changeset/20730-meta-packaged-base-dashboard-view.md`: a `patch` for
`@objectstack/rest`. It says in words that the console still draws the
packaged translation.
## Verification
- `pnpm --filter @objectstack/rest test`: 233 files, 4524 passed, 77
skipped. `test:repo`: 1 file, 8 passed. `typecheck`: `tsc --noEmit` plus
`check:test-typecheck` OK, and the new file is in the test program. All
at `0a60a2f61`, after merging `origin/main`.
- Readers of the edited module in other packages:
- runtime `meta-list-projection-parity`: 658 passed;
- runtime `meta-item-read-gate-parity` and `meta-list-read-gate-parity`:
118 passed;
- http-conformance `hono-meta-list-read-gate`: 6 passed.
- `dispatch-gates --commands` at `8ee4569fa` derived 97 families. All 97
were run and reconciled with `--ran`: 97 run, 0 NOT-MEASURED, 0 UNRUN.
`check:engine-double-contract` failed at `0a60a2f61` (the new double was
not yet in the ledger) and passed at `8ee4569fa` after the `--write`.
The 20 changeset and doc families were re-run at `2b8df02e7` and all
exited 0.
- Lint, narrowed and proven at `2b8df02e7`:
- Population: `eslint.config.mjs` matches
`**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}`, so 2 of the 5 changed paths are
in it (the others are `.md`, `.mdx` and `.json`).
- `eslint --no-inline-config --format json` on those 2 files: 2 files, 0
errors, 0 warnings.
- The config enables no type-aware linting (0 hits for
`parserOptions.project` or `projectService`), so this diff cannot move a
verdict on an untouched file.
## Acceptance notes
- **The name stays `packagedObjectBaseOf`** although it now answers
dashboards and views: `RestServer.packagedObjectBase` in
`rest-server.ts`, which PR #20683 holds, calls it by that name. A rename
belongs to the next edit of that file. Noted, not filed.
- **Docblocks outside this surface still say "object base only"**:
`RestServer.metaItemTranslationSources` / `metaListTranslationSources`
and the runtime's `metaTranslationSources` in
`packages/runtime/src/domains/meta.ts`. They are comments only, and the
behaviour is shared. Carrier for `rest-server.ts`: PR #20683. Carrier
for the runtime file: none. Noted, not filed.
- `action`, `app`, `dataset` and `page` have no packaged-base accessor.
They are ADR-0126 tier B, where a packaged item answers
`NOT_OVERRIDABLE` to a write, so no org overlay of them exists to
protect.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01VvcEokUG1tvVxkceYfR5XB)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent b280546 commit 7afdc5c
5 files changed
Lines changed: 474 additions & 16 deletions
File tree
- .changeset
- content/docs/ui
- packages/rest/src
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
174 | 174 | | |
175 | 175 | | |
176 | 176 | | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
177 | 204 | | |
178 | 205 | | |
179 | 206 | | |
| |||
0 commit comments