Repository navigation
fix(metadata-protocol): the layered read on a base's address answers an email template locale its .custom pair adds with the pair's layer (#28830) - #28845
Conversation
…an email template locale its `.custom` pair adds with the pair's layer `GET /meta/email_template/<name>/layers` naming the base or no package answered 404 for a locale the base does not ship and its `.custom` pair adds, while the by-name read, the base's lists and `sendTemplate` serve it since #28802. The layered read now takes the pair's row as its overlay through `findPairAddedMemberRow`, the one read the by-name read shares (PR #28828's `pairOfAddedMember` predicate). A dependent's added locale stays 404 there; non-bundle types are unchanged. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FEkQ5m2Ryfx2Wubuz716mw
📓 Docs Drift CheckThis PR changes 1 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 10 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 c75a860e95ab6982e978dc92a2681d4ebb51e093 && git checkout c75a860e95ab6982e978dc92a2681d4ebb51e093
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 2cba2eb3f1f50ff1678dfa7b2925e37965b6f9c5 c53513be600f1dec1a07824f133c73691f8c7214 && git checkout -B drift-repro 2cba2eb3f1f50ff1678dfa7b2925e37965b6f9c5 && git merge --no-ff c53513be600f1dec1a07824f133c73691f8c7214
node scripts/docs-audit/affected-docs.mjs --json 2cba2eb3f1f50ff1678dfa7b2925e37965b6f9c5
|
|
domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo Contract reviewServed-tier: Reviewed at 2026-11-02T00:21Z against the merge-base ① Derived judgments1. Semantics of the layered read, by case (bundle types only;
2. One seam, PR #28828's predicate. Exactly one new behavioural branch in 3. Lock, flags, stamp. 4. Evidence.
② Semver level
③ Boundary flagsAuthor
Implemented-by: VERDICT: PASS Generated by Claude Code |
|
domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo 席位核对ACCEPT · The maintainer's standing authorisation for landing reviewed PRs, verbatim: 「按照 pm-dispatch 规则复核过的都授权」.
Generated by Claude Code |
Requested by Leehom · project thread
Fixes #28830
Clause-②: yes (widening)
Metadata-protocol: none
维护者速读
/meta/email_template/<名称>/layers)按基础包名或不带包名读取时,客户在BASE.custom里新增的语言(基础包没有的,比如 showcase 只发en-US,客户加fr-FR)现在返回配对包那一层(code为空、overlay与effective是客户的模板),和按名读取、列表、发信一致;依赖包新增的语言在基础包地址下仍是 404,按依赖包名读取才有。.custompair is listed and sent on the base's address; a dependent's added locale stays under its own package (#28802) #28828 之后,配对包新增的语言在按名读取、列表和发信里都由基础包地址提供,只有分层视图还是 404,作者在后台看不到自己改了什么(metadata-protocol: the layered read of an email template answers 404 for a locale the<base>.custompair adds, while the list, by-name and send serve it (#28802 follow-up, ADR-0146 D8) #28830,PR fix(metadata-protocol)!: an email template locale added in a.custompair is listed and sent on the base's address; a dependent's added locale stays under its own package (#28802) #28828 合同复核提的「分层读取」标记)。.custompair is listed and sent on the base's address; a dependent's added locale stays under its own package (#28802) #28828 已有的同一判定(pairOfAddedMember),只在名单类型(ITEM_KEY_DISCRIMINATORS,目前只有email_template)且某语言基础包不发时才生效;其它类型字节不变,写入闸门不变。回滚即撤销本 PR。packages/metadata-protocol/src/protocol.ts的[#28830]一处(getMetaItemLayered)和新的私有方法findPairAddedMemberRow(按名读取与分层读取共用的一次读取)。pnpm --filter @objectstack/dogfood exec vitest run test/email-template-dependent-unshipped-locale-read.dogfood.test.ts,预期 12/12 通过(main 上 2 红)。What changes
Since PR #28828 (option A of #28779, ruling A on #28390), a base's address serves an email template member its
.custompair adds at a locale the base does not ship: the by-name reads naming the base or no package, the env-wide and base-scoped lists,getMetaItemsForExecutionandsendTemplate. The layered read did not:GET /meta/email_template/<name>/layers?package=<base>&member=fr-FRanswered404(no code layer, since the base ships nofr-FR, and no overlay, since the served-row candidates of the base's address hold no row at that member). ADR-0146 D8 lists the reads that must agree; this brings the layered read in line.?package=BASEor no package)en-US)code: base,overlay: nullen-US)code: base,overlay: overridefr-FR)404code: null,overlay/effective: the pair's rowfr-FR)404404(unchanged; answers under?package=DEPENDENT)fr-FR404packages/metadata-protocol/src/protocol.ts.getMetaItemLayered: when the served-row resolution finds no row and the read addresses a bundle member on a package's address, the pair's own row at that member is the overlay, read byfindPairAddedMemberRow.codestays as resolved (null: the base ships no such member), soeffectiveis the pair's row.findPairAddedMemberRow(new, private): the one read of the pair's row at an added member. It asks PR fix(metadata-protocol)!: an email template locale added in a.custompair is listed and sent on the base's address; a dependent's added locale stays under its own package (#28802) #28828'spairOfAddedMember(the base ships the name and not the member, elseundefined) and thenfindServedOverlayRownaming the pair.readMetaItem's metadata-protocol: an email-template locale added in a<base>.custompair is read by name but never listed or sent, so a send in that locale renders the base's (#28779 follow-up) #28802 branch now calls it too, so the by-name read and the layered read share one predicate and one read (behaviour of the by-name read unchanged: its suite and both test files stay green).pairOfAddedMembernames only the base's.custompair, so a dependent's row is not read on the base's address.Tests
packages/qa/dogfood/test/email-template-dependent-unshipped-locale-read.dogfood.test.ts(real showcase over HTTP,showcase_task_done_email): alayers(member, pkg?)probe on the rig and 2 new cases, plus layered assertions folded into 2 existing ones.{code: null, overlay: pair's subject, effective: pair's subject}forfr-FR; the base'sen-USlayers unchanged.fr-FRstays 404 on the base and package-less layered reads and answers under?package=DEPENDENT; baseen-USunchanged.fr-FR: the base's layered read answers the pair's.en-USoverride answers{code: shipped, overlay: override, effective: override}, andfr-FRon the base with only that override stored is 404.1920e480: 2 failed / 10 passed (the pair case and the pair-beside-dependent case). GREEN atc53513be: 12/12 (os-verify-lock VERDICT command-exit 0).packages/objectql/src/protocol-bundle-member-base-address.test.ts(real protocol, real engine, in-memory driver): alayersOfhelper and a[#28830]describe with 4 cases: the pair's added member on the base, package-less and pair addresses; a dependent's member 404 on the base and answered under the dependent; pair and dependent at one locale; control (declareden-USoverride, and a non-bundleviewanswering{code: base columns, overlay: null}as before). GREEN 10/10 atc53513be.if (row) served = { row };→void row;throughscripts/ablation-replace.mjs(anchor 1 → 0, restore blob == HEAD verified),metadata-protocolrebuilt and the mutation proved absent fromdist/byablation-dist-preflight.mjs --absent. Unit: 2 red (the pair's added member; pair beside dependent), 8 green. Dogfood: 2 red (the same two), 10 green. The dependent cases and every control stay green. A restore build followed, the marker proved present again.Acceptance notes
versiontoken (measured, not changed). On main1920e480, showcase, pair-addedfr-FR: the by-name read naming the base answersversion: null; the package-less and pair-named reads answer the pair row's token (hmac-sha256:a8a29081…); the base's ownen-USread naming the base answersnull(no stored row at the base's address).versionisreadVersionTokenat the request's address, so naming the base it reads the base's row atfr-FR, which does not exist. Not the same seam (the token is the OCC pin of a save at that address, and a save naming the base at a member only the pair adds is the write gate's question), so it is not changed here; reported for the seat.Metadata-protocol: none. Nothing an author can write changed (no key, value or metadata type added or refused) and no meaning changed: what a pair-added locale means on the base's address was decided by fix(metadata-protocol)!: an email template locale added in a.custompair is listed and sent on the base's address; a dependent's added locale stays under its own package (#28802) #28828; this read now reports it. Changesetpatch._packageIdstamp of its own; theoverlayis the pair's stored document as stored.overlayRow(whatdeclinesStoredRowjudges a shipped flow name on) is set from the pair's row too; it only matters forflow, which is no bundle type.pr_create: open PRs touchingpackages/metadata-protocol/src/are fix(metadata-protocol): the by-id OCC comparison answers only for a row the caller reads #28731 (protocol.ts, the by-id OCC comparison near line 14450), fix(spec): the connector read withholds root keys the entry shape does not declare #28764 (metadata-redaction.ts) and feat(spec)!: refuse at publish a save_notice that a suspending node reaches #28838 (one test file).git merge-treeagainst this head: no conflict with any; none touchesreadMetaItem,getMetaItemLayeredor the metadata-protocol: an email-template locale added in a<base>.custompair is read by name but never listed or sent, so a send in that locale renders the base's (#28779 follow-up) #28802 helpers. Seat 1's metadata-protocol: the per-package layer stamps another package's row under a base item name with the base's_packageId(export and form withdrawal layer name the wrong author) #28831 (the per-package layer's provenance) has no open PR yet.Verification
Package suites at
c53513be:@objectstack/metadata-protocoltypecheck + test (297 files passed, 4 skipped; 22080 tests passed),@objectstack/objectqltypecheck + test (521 files, 10008 tests passed), both under the lock, VERDICT command-exit 0.Gate sweep at
c53513be:dispatch-gates --commandsderived 66 families (the same 66 as PR #28828); 66 run, all exit 0 (dispatch-gates --ranreads "66 run, 0 NOT-MEASURED", a derived zero). One first ran exit 3 on a prerequisite and was re-run green:check:dual-build-cjs-loads(seven packages outside the dogfood closure were unbuilt). The record ispm-orders/server/28830-gates-ran.txtin the project files.Full dogfood battery: not run. The arm is
(widening)and the title carries no!; the two touched test files ran as above.Changeset
.changeset/28830-layered-read-pair-added-member.md:@objectstack/metadata-protocolpatch, both declaration lines. CI families are read from this PR's check-runs, not re-run here.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01FEkQ5m2Ryfx2Wubuz716mw