Skip to content

fix(ui): Setup opens each email template locale as its own member - #27036

Draft
objectstack-fleet[bot] wants to merge 4 commits into
mainfrom
claude/project-thread-4e9wb4
Draft

objectstack-fleet[bot] wants to merge 4 commits into
mainfrom
claude/project-thread-4e9wb4

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Warren · project thread
Fixes #26649

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:Setup 的元数据列表给邮件模板加了「语言」列,每一行链接带上自己的 ?member=语言;编辑页把这个 member 传给读取(分层视图、草稿)和写入(发布、重置、放弃草稿)。
  • 为什么改:同一模板的两种语言各占一行,但点哪一行都打开同一种语言,第二种语言在 Setup 里打不开也改不了(裁决 6444406836 选 A,服务端 /layers?member= 已由 feat(metadata): the layered read takes the bundle member selector #26861 落地)。
  • 风险与回滚:只影响声明了 member 字段的类型(目前只有 email_template);其它类型的链接和请求逐字节不变。回滚 = revert 本 PR。
  • 先看:ResourceEditPage.tsx(读写都带 member,尤其重置/发布那几处)、ResourceListPage.tsx(行链接与语言列)、metadata-client.ts(五个方法的 member 参数)。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/views/metadata-admin/ResourceEditPage.bundleMember-26649.test.tsx,预期 5 个用例全绿;或在 Setup → Email Template 里点 zh-CN 那一行,地址栏带 member=zh-CN,打开的是中文内容。
  • 需要你做的:无,席位落地。

What changes

  • Registry (registry.ts, default-schemas.ts): a resource config may declare bundleMemberField, the body field that tells two members of one name apart; email_template declares locale. BUNDLE_MEMBER_PARAM (member) and bundleMemberOf live beside it. The server serves no bundle member key: its table (ITEM_KEY_DISCRIMINATORS) lives in @objectstack/metadata-core, which no ui package depends on, and a GET /meta/email_template row carries only the body's own locale. So the field is declared UI-side, on the registry entry.
  • List (ResourceListPage.tsx): for such a type, a Locale column sits second (label from engine.list.col.locale, en + zh rows in i18n.ts), and each row links ./NAME plus the existing suffix (?scope=environment / ?package=) plus member=LOCALE. A row with no locale (the canonical member) links by name alone, as before. Every other type's columns and links are unchanged.
  • Editor (ResourceEditPage.tsx, DraftConflictDialog.tsx): ?member= is read once and passed to the load's layered and draft reads (ruling 6444406836), and to the post-save, post-publish, post-reset and post-discard re-reads. It is also passed to the page's by-name writes, publish, reset and the draft discard: each of those doors addresses the canonical member when named none, so without it Reset on the zh-CN page would remove the en-US overlay and Publish would promote the en-US draft. The save sends no member (it is addressed by its body's locale). readDraftForSave gains an optional member.
  • Client (metadata-client.ts, @object-ui/data-objectstack, patch changeset): layered, get, getDraft, publish and reset take an optional member, sent as ?member= beside ?package=, omitted (never empty) when absent. getDraft with a member skips the draft read when the ledger lists that name only as another member's draft.

Tests

  • metadata-client.bundleMember-26649.test.ts (8): each method sends ?member=, beside ?package=; no member means no parameter on every door; the ledger skip and its control.
  • ResourceListPage.bundleMember-26649.test.tsx (4): two members of one template are two rows whose links differ by member and keep scope=environment; the canonical row links by name; the Locale column with each row's value; CONTROL: page (no members, body carrying locale) gets no column and no member.
  • ResourceEditPage.bundleMember-26649.test.tsx (5): the real page over a real MetadataClient and an in-memory server keyed by (name, member). Opening ?member=zh-CN reads that member's layers and draft and shows its body; CONTROL with no member sends no selector; Publish, Discard draft and Reset each send member=zh-CN and change only the zh-CN rows.

Ablation, once: the load's layered read without member (ablation-replace.mjs, anchor 1 to 0, blob 64744a88 to 55c8f52b) turns 2 of the 5 editor tests red ("reads the member's layers and draft…", "Reset removes the opened member's overlay…": expected null to be 'zh-CN'); restored, blob equals HEAD, git diff HEAD empty. A first attempt whose replacement text already occurred in the file was refused by the tool before any test ran.

Acceptance notes

  • The server serves no bundle member key for a type (list rows, /meta/types), so the ui declares email_template's field itself. A served key would let this registry row go.
  • Changing the locale field of an existing template in the editor and saving writes another member (the save is body-addressed); the page keeps addressing the member in its URL. The create form asks for no locale, so a create lands on whatever the body defaults to.
  • The history page and the references sheet send no member: history is out of this card's ruling, and references have no member concept.

Verification

Gates and suites are recorded below at the head this body names.

All at head 6b9ab868 (origin/main 7eb613c3 merged in; no incoming file overlaps this diff):

  • Dependency closure pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build under the verify lock: exit 0.
  • pnpm --dir ui exec vitest run packages/data-objectstack/ packages/app-shell/src/views/metadata-admin/ (pre-merge head 3cbf7325): 726 files / 8020 tests passed, 1 skipped.
  • After the merge: vitest run packages/data-objectstack/ plus the metadata-admin ResourceListPage*, ResourceEditPage* and DraftConflictDialog* files: 153 files / 1491 tests passed.
  • pnpm --filter @object-ui/data-objectstack --filter @object-ui/app-shell run type-check: exit 0. pnpm --dir ui run type-check:scripts: exit 0.
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 34 derived families, each run, all exit 0 (including node scripts/check-sdui-manifest.mjs and its --self-test, and pnpm --dir ui exec vitest run scripts/); --ran reconciliation: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN.
  • pnpm --dir ui run check:new-line-citations: 0 new citations, exit 0.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (not touched)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes. (No formatter ran; the repo-wide lint is CI's run, not measured locally.)
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: n/a.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English (the maintainer brief is Chinese by the dispatch order).

Generated by Claude Code

@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Oct 28, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · domain:ui#2 · session_01JQwxbXpF772qWJjojJvRXw · head 6b9ab8689d · 2026-10-28T02:36Z

席位意见:只对声明了 member 字段的类型(目前只有邮件模板)生效,其它类型的链接和请求不变;发布/重置/放弃草稿也带上 member,否则在中文页点重置会删掉英文那条,这一扩展是对的。

  1. Shape: draft, base main; the body says Fixes #26649, which matches the order (whole card, ruling 6444406836 option A). 11 files, +694/−21; not governed (check-governed-merges --pr 27036); ≤ 3000 lines.
  2. Premise measured: seat read on origin/main. Every door the page now addresses by member takes ?member=: the item read and /layers (meta-item-read-gate.ts META_ITEM_LAYERS_PARAMS, metadata: the layered read (GET /meta/:type/:name/layers) takes no member selector, so Setup cannot open a non-canonical email_template locale #26699), reset (meta-item-write-routes.ts L483/L523), and publish (meta-item-history-routes.ts L667/L712). The save stays body-addressed and sends none, per ruling 6116165299. Absent a member, every URL is byte-identical to before (client tests' no-member control).
  3. Gate check-runs (pinned to head 6b9ab8689d): in progress at this record; landing waits for every check green. The dev's local reading: 34/34 derived families exit 0, --ran 0 NOT-MEASURED, 17 new tests, ablation 2/5 red then restored.
  4. Accepted deviations: files beyond the order's surface. registry.ts and default-schemas.ts declare bundleMemberField UI-side, because the server serves no member key. DraftConflictDialog.tsx passes one optional member through readDraftForSave. i18n.ts gets one engine.list.col.locale row in en and zh, which the order allowed. All are in the same directory, and none is held by another seat (PermissionAdvancedFacets.tsx and AppNavInspector.tsx are untouched). Write-door threading was accepted earlier on the thread.
  5. Out of scope (Acceptance notes): the server serves no bundle member key, so a served key would retire the registry row. A locale edit in the editor writes another member (body-addressed save). History and references send no member. These are notes, not defects in this card's scope. Serial: PR fix(app-shell): a locked metadata item's designer canvas is read-only #27035 (Setup metadata object designer: a locked object's canvas accepts Add field / move / remove section, shows "Unsaved" and offers no Save — the work is dropped silently #26888, seat 2) also edits ResourceEditPage.tsx in a disjoint region (the preview onPatch); whichever lands second merges main first.
  6. Landing clause: not governed. When every check on the current head is green, the seat lands it (pr_ready + automerge_enable), or asks the maintainer to click if the classifier refuses.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

2 participants