fix(rest): the /meta dashboard and view reads hand the translator the packaged base, so a published org overlay beats the packaged catalog - #20832
Conversation
…their packaged base, so a published org overlay beats the packaged catalog packagedObjectBaseOf becomes the one per-type packaged-base resolver: object answers getPackagedObjectBase as before, dashboard answers getPackagedDashboardBase and view getPackagedViewBase (by the served item's qualified registry name). translateMetaDocument and translateMetaList resolve the protocol for every type the table covers. Claude-Session: https://claude.ai/code/session_01VvcEokUG1tvVxkceYfR5XB Co-authored-by: Claude <noreply@anthropic.com>
…ta reads Claude-Session: https://claude.ai/code/session_01VvcEokUG1tvVxkceYfR5XB Co-authored-by: Claude <noreply@anthropic.com>
…packaged catalog; changeset for @objectstack/rest Claude-Session: https://claude.ai/code/session_01VvcEokUG1tvVxkceYfR5XB Co-authored-by: Claude <noreply@anthropic.com>
…ta-packaged-base-resolver
Written by check-engine-double-contract --write for the dashboard and view packaged-base pin file. Claude-Session: https://claude.ai/code/session_01VvcEokUG1tvVxkceYfR5XB Co-authored-by: Claude <noreply@anthropic.com>
…t claim Claude-Session: https://claude.ai/code/session_01VvcEokUG1tvVxkceYfR5XB Co-authored-by: Claude <noreply@anthropic.com>
…, as measured Claude-Session: https://claude.ai/code/session_01VvcEokUG1tvVxkceYfR5XB Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check8 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 15 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 262804cfd94548466e8794783360042b5a27d024 && git checkout 262804cfd94548466e8794783360042b5a27d024
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 73155fedcacc215565c4eef6d9899977e0707010 2b8df02e7aca4b62cbf44ea8860d6f8a638916aa && git checkout -B drift-repro 73155fedcacc215565c4eef6d9899977e0707010 && git merge --no-ff 2b8df02e7aca4b62cbf44ea8860d6f8a638916aa
node scripts/docs-audit/affected-docs.mjs --json 73155fedcacc215565c4eef6d9899977e0707010 |
Contract reviewServed-tier: Inputs read: card #20730 body and its five comments ( Check-runs on the head, converged (the one run still ① Derived judgments(a) The resolver — one table, three accessors, both callers. RIGHT.
(b) The behaviour the card asks for. RIGHT, each claim pinned; nothing served differently for a caller or type the card did not name.
(c) Changeset and docs. RIGHT.
(d) ② Semver level
③ Boundary flagsDeviations (7), each answered:
Open questions: none declared, none found. Out-of-scope findings (3):
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #20730
Clause-②: no
What this changes
An org's published edit to a packaged dashboard or view is now what the
/metaitem and list reads serve, in every locale. Before, the reads served the bundle's translation of the string the package shipped.packagedObjectBaseOfinpackages/rest/src/meta-item-read-gate.tsis now the one per-type packaged-base resolver that the triage asked for. It reads one table,PACKAGED_BASE_ACCESSORS:objectgetPackagedObjectBase(name)(unchanged)dashboardgetPackagedDashboardBase(name)viewgetPackagedViewBase(name), by the served item's qualifiedOBJECT.VIEW_KEYregistry nametranslateMetaDocument(the item read) andtranslateMetaList(the list read) now resolve the protocol for every type in that table instead of forobjectonly. Both/metatransports (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.tsis not touched: it delegates to these functions and already hands them its protocol.packages/metadata-protocolandpackages/specare read, not edited.Pins
packages/rest/src/meta-dashboard-view-i18n-explicit-override.test.ts(52 tests) runs over the REALObjectStackProtocolImplementationand a REALSchemaRegistry, through the/metaroutes. 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.action,app,dataset,page,report,toStringorconstructor. It answersundefinedfor an empty name, a missing accessor or a throwing accessor.enandzh-CN:showcase_task.in_progress. The unedited view isshowcase_task.urgent.One line in
scripts/engine-double-contract.pinned.json, written bycheck-engine-double-contract --writefor the newfindOnedouble. The double callsassertEngineFindOnePredicate.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 emptygit diff HEAD.objectonly (blob38a35644to4aeefa5c)objectonlyobjectonlyMeasured 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 treee830f24cb. The probe ran three phases: pristine; overlaid (adminPUT ?mode=draftthenPOST publish, each answering 200, onsystem_overview, on the showcase controlshowcase_ops_dashboardand onshowcase_task.in_progress); and reset (adminDELETE, answering 200). In every phase it read/meta/dashboard/NAME,/meta/view/NAME,/meta/dashboard,/meta/view,/meta/appand/meta/objectfor both callers inenandzh-CN.Before (the rest dist rebuilt from the ablated resolver;
ablation-dist-preflightfound the marker in 2 built files) compared with after (HEAD): 12 served leaf fields differ, all in the overlaid phase:system_overviewwidget_total_users.titleon the item and list reads, admin and member,enandzh-CN(8). It went fromTotal Users/用户总数toTotal Users (edited-20730).showcase_task.in_progresslabelon the item and list reads, admin and member,zh-CN(4). It went from进行中toIn Progress (edited-20730). Anenreader was already served the edit before this change.The restore leg rebuilt rest, and
ablation-dist-preflight --absentfound 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) withscripts/build-console.shand served bypnpm dev -- --freshat8ee4569fa. 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 toenand tozh-CN./api/v1/meta/dashboardresponses carriedwidget_total_users.title=Total Users (edited-20730)in both locales, and its/api/v1/meta/viewresponses carriedlabel=In Progress (edited-20730)./_console/apps/setup/dashboard/system_overviewstill showedTotal Users(en) and用户总数(zh-CN)./_console/apps/showcase_app/showcase_task/view/in_progress:en: the tab showed the edit, and the breadcrumb showedIn Progress;zh-CN: both the tab and the breadcrumb showed进行中.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.mdxgains "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: apatchfor@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 --noEmitpluscheck:test-typecheckOK, and the new file is in the test program. All at0a60a2f61, after mergingorigin/main.meta-list-projection-parity: 658 passed;meta-item-read-gate-parityandmeta-list-read-gate-parity: 118 passed;hono-meta-list-read-gate: 6 passed.dispatch-gates --commandsat8ee4569faderived 97 families. All 97 were run and reconciled with--ran: 97 run, 0 NOT-MEASURED, 0 UNRUN.check:engine-double-contractfailed at0a60a2f61(the new double was not yet in the ledger) and passed at8ee4569faafter the--write. The 20 changeset and doc families were re-run at2b8df02e7and all exited 0.2b8df02e7:eslint.config.mjsmatches**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}, so 2 of the 5 changed paths are in it (the others are.md,.mdxand.json).eslint --no-inline-config --format jsonon those 2 files: 2 files, 0 errors, 0 warnings.parserOptions.projectorprojectService), so this diff cannot move a verdict on an untouched file.Acceptance notes
packagedObjectBaseOfalthough it now answers dashboards and views:RestServer.packagedObjectBaseinrest-server.ts, which PR feat(rest): GET /data/:object/export?template=true answers an xlsx import template (#18386) #20683 holds, calls it by that name. A rename belongs to the next edit of that file. Noted, not filed.RestServer.metaItemTranslationSources/metaListTranslationSourcesand the runtime'smetaTranslationSourcesinpackages/runtime/src/domains/meta.ts. They are comments only, and the behaviour is shared. Carrier forrest-server.ts: PR feat(rest): GET /data/:object/export?template=true answers an xlsx import template (#18386) #20683. Carrier for the runtime file: none. Noted, not filed.action,app,datasetandpagehave no packaged-base accessor. They are ADR-0126 tier B, where a packaged item answersNOT_OVERRIDABLEto a write, so no org overlay of them exists to protect.Generated by Claude Code