Repository navigation
fix(i18n)!: the console translation bundle leaves out Studio's metadataForms (75 KB of 221 KB on app-crm) - #26159
Conversation
…ved metadataForms GET /i18n/translations/:locale now answers the platform groups without SERVER_RESOLVED_TRANSLATION_GROUPS (metadataForms). Studio's authoring forms are already translated on the server by /meta/types, from the same i18n service tree, which the door copies and never edits. GetTranslationsResponseSchema declares the served face (ServedTranslationDataSchema), closed, so a body that still carries the group fails it. Claude-Session: https://claude.ai/code/session_01Vy85beHdawtEtqNscYBy7V Co-authored-by: Claude <noreply@anthropic.com>
… and the response schema Claude-Session: https://claude.ai/code/session_01Vy85beHdawtEtqNscYBy7V Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vy85beHdawtEtqNscYBy7V Co-authored-by: Claude <noreply@anthropic.com>
…s out; changeset Claude-Session: https://claude.ai/code/session_01Vy85beHdawtEtqNscYBy7V Co-authored-by: Claude <noreply@anthropic.com>
…eader totals Claude-Session: https://claude.ai/code/session_01Vy85beHdawtEtqNscYBy7V Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 6 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 152 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 008428f5a9295132562c0263c416b665043e51ca && git checkout 008428f5a9295132562c0263c416b665043e51ca
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 8c0306f5d5db836331098483902b0932dd18e6da 392a610a7e66af7e878647124e343c5f46958733 && git checkout -B drift-repro 8c0306f5d5db836331098483902b0932dd18e6da && git merge --no-ff 392a610a7e66af7e878647124e343c5f46958733
node scripts/docs-audit/affected-docs.mjs --json 8c0306f5d5db836331098483902b0932dd18e6da
|
…n for the served bundle The seat's contract review of the served-translations PR amends the claim to `Clause-②: yes (narrowing)`: the published `GetTranslationsResponseSchema` accepted a body carrying `metadataForms` and now refuses it, and the exported type loses that member. The changeset carries the BREAKING banner, the remedy (read the group from `/meta/types`) and one ADR-0087 disposition. `requestedLocale` is declared on the same schema, optional. The runtime door has served it on the locale-fallback exit since that fallback existed, with no declaration behind it; pins in the spec and runtime suites now read it from the schema. Two ui docblocks described the old composition. Both now say what is true: `SPEC_TRANSLATION_GROUPS` is a superset of the groups the server still serves, and the metadata designer's overlay names `/meta/types` as the channel that carries an environment's own `metadataForms`. Claude-Session: https://claude.ai/code/session_01Vy85beHdawtEtqNscYBy7V Co-authored-by: Claude <noreply@anthropic.com>
Editing `ui/packages/i18n/src/utils/spec-translations.ts` moves that package's registry digest, so its record under `scripts/sdui-manifest.sources/` follows it. `sdui.manifest.json` is byte-identical: 106 components, sha256 1f6e1ccb925a. Claude-Session: https://claude.ai/code/session_01Vy85beHdawtEtqNscYBy7V Co-authored-by: Claude <noreply@anthropic.com>
Round 2 — the seat's contract review (claim amendment 6418168409)The declarations are corrected at the top of this body:
Measured at the head this comment is written against, The declaration lines and the title of this pull request are the seat's write, not the author's (a dev writes a body once, at creation): the title takes Generated by Claude Code |
…-nvoyre # Conflicts: # packages/spec/dropped-refinements.baseline.json # scripts/sdui-manifest.sources/i18n.json
…tocol narrowed, minor The authorable-surface shards this diff moves (the served face's keys and GetTranslationsResponse:requestedLocale) make it a metadata-protocol change, so Check Changeset needs a kind other than none and at least minor on a moved package. The changeset now declares Metadata-protocol: narrowed and ships @objectstack/spec and @objectstack/runtime at minor; the ADR-0087 reason is restated for the new kind. Claude-Session: https://claude.ai/code/session_01Vy85beHdawtEtqNscYBy7V Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vy85beHdawtEtqNscYBy7V Co-authored-by: Claude <noreply@anthropic.com>
…-nvoyre # Conflicts: # scripts/sdui-manifest.sources/i18n.json
Claude-Session: https://claude.ai/code/session_01Vy85beHdawtEtqNscYBy7V Co-authored-by: Claude <noreply@anthropic.com>
…-nvoyre # Conflicts: # packages/spec/src/type-alias-convention.pin.test.ts # scripts/sdui-manifest.sources/i18n.json
Claude-Session: https://claude.ai/code/session_01Vy85beHdawtEtqNscYBy7V Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vy85beHdawtEtqNscYBy7V Co-authored-by: Claude <noreply@anthropic.com>
…-nvoyre Claude-Session: https://claude.ai/code/session_01Vy85beHdawtEtqNscYBy7V Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vy85beHdawtEtqNscYBy7V Co-authored-by: Claude <noreply@anthropic.com>
|
Claim: contract review of PR #26159 at head Cross-account review for spec seats 2 and 4, on the maintainer's word in this seat's project: 「座位 2 和 4 需要契约复审的pr你也负责帮忙复审」. ⛔ Not a claim on card #25785, no label or assignee change. Face: published schema ( |
Contract reviewServed-tier: Inputs read: card #25785 (body and all 14 comments, among them claims 6407675488 and 6415714537, amendments 6418168409 and 6421832617, dev reports 6418116291, 6421358584 and 6428970383, release 6410241389, re-route 6412175443), PR #26159 (body, 22-file list, comments 6418082927, 6421330578 and 6431285224, no review comments or reviews), the net diff against main at the head, the 63 check-runs on the head (58 success, 5 skipped, 0 red), and main's window 3249e20..e843b5b (42 commits, 300 files) that merge a25dc27 brought in after the dogfood head 2cd9696. ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Rendered by an isolated CONTRACT_REVIEW_TIER subagent of seat session |
…-nvoyre Claude-Session: https://claude.ai/code/session_01Vy85beHdawtEtqNscYBy7V Co-authored-by: Claude <noreply@anthropic.com>
…ter merging main Claude-Session: https://claude.ai/code/session_01Vy85beHdawtEtqNscYBy7V Co-authored-by: Claude <noreply@anthropic.com>
Seat ACCEPT — PR #26159 at
|
Requested by Steedos · project thread
Fixes #25785
Clause-②: yes (narrowing)
Metadata-protocol: narrowed
Summary
GET /i18n/translations/:localeserved the i18n service's whole locale tree, so every console open downloadedmetadataForms, the translations of Studio's metadata-type authoring forms. Onexamples/app-crmthat is 75 KB of the 221 KB raw zh-CN bundle, the second-largest group afterobjects. The card's other half (theETagandCache-Control: private, no-cachevalidator) already landed with the earlier i18n caching PR; this PR is the composition half.How Studio gets its forms now: it already does, from the server. No browser code reads
metadataFormsfrom this bundle. The/metaand/meta/typeshandler (translateMetaTypesResponsein@objectstack/rest) reads the same i18n service in process, resolvesmetadataForms.TYPEfor each type, and serves every form already in the requested locale. The liveness ledger (packages/spec/liveness/translation.json) names that handler as the group's only reader. So the group only has to stop being served to the browser. Studio loads nothing new, and there is no new route, key or endpoint.system/translation.zod.ts. NewSERVER_RESOLVED_TRANSLATION_GROUPS = ['metadataForms']andServedTranslationDataSchema: the platform face (PlatformTranslationDataSchema) without those groups. It is closed (strictObject), so a served document that still carries one fails it by name.api/protocol.zod.ts.GetTranslationsResponseSchema.translationsis nowServedTranslationDataSchema, so the declared response is what the door serves.domains/i18n.ts. Both exits (the requested locale and thezhtozh-CNfallback) serveservedTranslations(tree), a copy without the server-resolved groups. The service's own tree is never edited, because/metatranslates from that same object (both the core memory i18n andservice-i18nhand back the stored tree). TheETagstill hashes the served body, so it now changes only when served bytes change.i18n-standard.mdx(Metadata authoring forms) says the console bundle leaves the group out and/metareads it from the service. The reference pages are regenerated.25785-served-translations-without-metadata-forms.md, patch for@objectstack/specand@objectstack/runtime.metadataFormsstays authorable on every bundle face (TranslationDataSchema,PlatformTranslationDataSchema, app bundles,i18n:extract). Only the served document leaves it out.Measured live (
examples/app-crm, signed-in member, same box)metadataForms0a6948210a694821Every other group kept its exact byte size (for example zh-CN
objects111,504,settings25,868,apps4,469).GET /api/v1/meta/typesandGET /api/v1/metain zh-CN answered 31 entries on both builds, with identical translated labels: object 对象, field 字段, dashboard 仪表板, view 视图, flow 流程, and the first section and field of each form (基础信息, 名称).Tests
packages/runtime/src/domains/i18n-translations-served-groups.test.ts(10 tests):metadataForms. The body parses the closedGetTranslationsResponseSchema, with a control showing the schema refuses the same body when it carries the group.RestServer/api/v1/metabuilt over the same service, read after the boot read, still serves the dashboard form translated (仪表板, 布局, 列数).ETagis the hash of the served body,If-None-Matchanswers 304, a change confined tometadataFormskeeps the validator, and a change tomessagesmoves it.packages/spec/src/system/served-translation-data.test.ts: the served face is the platform face minus the constant (with a positive control); the response schema serves that face; each server-resolved group stays declared on both bundle faces; a document carrying the group is refused withunrecognized_keysnaming it.type-alias-convention.pin.test.ts: oneIsopin for the new schema (789 to 790).dropped-refinements.baseline.json: one entry forsystem/ServedTranslationData, the same 3 sites asPlatformTranslationData, with header totals 245 / 841.Runs (under
scripts/pm/os-verify-lock.sh,--maxWorkers=2):@objectstack/runtimefull suite at8b2ad27c: Test Files 455 passed (455), Tests 7415 passed, 19 skipped. VERDICT command-exit 0.@objectstack/specfull suite: Tests 21929 passed, 1 failed, the dropped-refinements header totals. Those were recounted from the body in8b2ad27c, and the file then ran 27/27. That commit touches only those two totals, which only that test reads.@objectstack/spec,@objectstack/runtimeand@objectstack/client: VERDICT command-exit 0.ui/apps/console/src/loadLanguage.test.tsx(the console's bundle loader): 7 passed.Ablation (one-off, mutated on disk, then restored)
servedTranslationscopies every group (blob3154a44dtoa28c8f4a). 6 of 10 tests failed: path and query spellings, fallback exit, schema parse, the/metaafter-boot read, and the unserved-change validator. Restored blob3154a44dequalsHEAD.servedTranslationsstill serves the right document but also deletes the group from the service's own tree (return served;replaced with a loop deleting each server-resolved group fromtranslations, blob3154a44dto5e38e852). Exactly 2 of 10 tests failed, both Studio pins: "a boot read does not edit the service's tree" and the/metaafter-boot read, which served'Dashboard'where'仪表板'was expected. The other 8 passed, so the served bytes and the validator do not catch this mistake and only the B pins do. Restored blob3154a44dequalsHEAD,git diff HEADempty.Both runs used
scripts/ablation-replace.mjswithsrc/domains/i18n-translations-served-groups.test.tsunder the verify lock. No permanent test file was added for them.Gates
Derived with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(114 commands; the list derived at the head8b2ad27cis identical to the one run). Each ran with its exit code captured before any pipe, and--ranreconciled them at8b2ad27c: "114 derived famil(ies) accounted for — 114 run, 0 NOT-MEASURED". All 114 exited 0, among themcheck:generated,check:api-surface,check:authorable-surface,check:docs,check:liveness,check:strictness-ledger,check:spec-parsed-alias,check:exported-any,check:dual-build-cjs-loads(after a full workspace build),check:nul-bytesandcheck:type-check-debtunder the verify lock ("26 raw tsc error(s) total, none above its recorded number").The sweep started before the last commit, which only recounts the two dropped-refinements header totals. The 10 commands that finished before that commit, and
check:query-options-erasure, which overlapped the 20-second window of the first ablation, were rerun at8b2ad27c; all exited 0. The local-onlycheck:dev-prereqsandcheck:skill-frame-freshnessalso exited 0.dispatch-gateswarned STALE TREE: the branch is behindmain. No file in this diff is touched bymain's newer commits, andgit merge-treewithorigin/mainreports no conflict, somainwas not merged in.Notes for review
packages/spec/srcmoved (system/translation.zod.ts,api/protocol.zod.ts), so theCONTRACT_REVIEW_TIERreview applies. Neither file was on the claim's named file surface; the fix belongs in the spec declaration, so it was made there.translationswas the platform face, which is closed and declaresmetadataForms. The served face is closed the same way without it, so a body that still carries the group now fails the schema. A client validating this route's responses keeps passing. A client that readmetadataFormsfrom this route gets nothing; the liveness ledger and a repository search show no such reader (the console loader, the ui dashboard inspector andTranslationPreviewdo not read it).ui/apps/console/src/loadLanguage.test.tsxwalksGetTranslationsResponseSchema.shape.translations(through theworkspace:^link to thispackages/spec, so it reads this change) and requires the loader to namespace every group it finds. It passes with the narrower list: 7 passed.SPEC_TRANSLATION_GROUPSinui/packages/i18ndeliberately keeps its own copy of the list and is a superset detector, so still listingmetadataFormschanges no behaviour. Twouidocblocks now read a little stale — that list's header calls itself "the keys of thetranslationsshape", andmetadata-form-i18n.tsexpects "the environment's own translation bundle" to arrive one day as a higher-precedence source for Studio's form labels (it would now be/meta/types, which already serves them translated). Neither is on this claim's surface and neither is a behaviour, so both are left alone and reported to the seat.requestedLocaleis served but not declared onGetTranslationsResponseSchema. It was like this before and is unchanged here.objectsto the current app. The card marks it optional and a bigger contract change. It is reported to the seat as a separate finding.维护者速读(草稿)
改了什么. 控制台每次打开都会请求当前语言的翻译包。这个包不再带
metadataForms(Studio 元数据编辑表单的翻译)。其余翻译分组一个字节不变。为什么改. 这部分在 app-crm 的中文包里占 75 KB(总共 221 KB),浏览器从来不读它。Studio 的表单翻译一直是服务端在
/meta/types里直接翻好再下发的。去掉之后,中文包压缩后从 68.6 KB 降到 45.1 KB,英文从 58.7 KB 降到 37.6 KB。第二次打开仍然是 304。风险与代价(含回滚). 风险是有外部客户端从这个接口读
metadataForms。仓内没有这样的读者。Studio 的表单翻译有测试钉住:先请求翻译包,再读/meta,表单仍是中文。回滚只需要还原本 PR,不涉及数据迁移。没有新增接口,也没有新增配置键。席位意见.
你要做的. 评审并合并。可选:如果需要,按应用裁剪
objects分组另开卡(本 PR 没做)。Round 2 — the seat's contract review (claim amendment 6418168409)
The declarations are corrected at the top of this body:
Clause-②: yes (narrowing),Metadata-protocol: none. The publishedGetTranslationsResponseSchemaaccepted a body carryingmetadataFormsand now refuses it, and the exportedGetTranslationsResponsetype loses that member; nothing an author writes moves, so the kind staysnoneand the level stayspatch.GET /meta/types, which serves each form already translated) and exactly one ADR-0087 disposition:not-required (no-migration-prescription), accepted bycheck-adr-0087-registration— "1 declared-breaking changeset(s), each carrying an ADR-0087 disposition". The reason closes the other five categories on facts: both packages publish; no ADR-0087 id is named or touched; the diff touchespackages/spec/**and a*.zod.tsface, which closestype-surface-only; the narrowed declaration is a Zod projection, which closesruntime-interface-only; and no export is retired. The remedy is written as prose rather than a literalFROM → TOlabel block, because that label is branch 1 of the gate's migration-prescription detector and would make the only claimable category unclaimable; the mapping it would carry ("readmetadataFormsfromGET /meta/types") is in the changeset's "What to do" and above.requestedLocaleis now declared onGetTranslationsResponseSchema, optional (packages/spec/src/api/protocol.zod.ts). The door has served it on the locale-fallback exit (zhserved aszh-CN) since that fallback existed, undeclared. Same schema, same class, mechanical — the in-place adjacent fix the amended claim names. Pinned on both sides: the spec test fixes the shape tolocale/requestedLocale/translations, parses a fallback body, keeps the field optional and refuses a non-string; the runtime test parses the real fallback body against the schema.uidocblocks this PR had made false are repaired.SPEC_TRANSLATION_GROUPS(ui/packages/i18n/src/utils/spec-translations.ts) now says it is deliberately a superset of the groups the server still serves, namesmetadataFormsas the first group to have left, and states the one direction that is red (a group the spec serves and the list lacks).metadata-form-i18n.ts(ui/packages/app-shell) now names/api/v1/meta/typesas the channel that carries an environment's ownmetadataForms, and says this overlay covers the forms the console renders from its own copy of the spec.origin/mainwas merged in (merge81dc21e7), because editing auiregistry source moves that package's digest inscripts/sdui-manifest.sources/andmainhad just re-shaped those records one-per-package (devx(sdui): one manifest record per ui registry package, so ui PRs on different packages stop conflicting #26120). No file in this diff overlapsmain's range, so the merge is textually clean. The digest was then re-recorded with the merged generator: 1 of 27 package records moved (i18n),sdui.manifest.jsonbyte-identical at 106 components,check-sdui-manifestgreen.Measured at the head this comment is written against,
4f5d2f0a: the dogfood battery (closure built first, thenpnpm --filter @objectstack/dogfood testunder the verify lock, no shard and no path) isTest Files 296 passed | 3 skipped (299),Tests 2523 passed | 18 skipped (2541), VERDICT 0. The@objectstack/specsuite is803files /23204tests green, the six runtime i18n files103tests, the type-checks clean, and the console loader plus the@object-ui/i18ntransform suite22tests. The derived gate list is now 125 commands and all 125 exited 0, reconciled as "125 run, 0 NOT-MEASURED".The declaration lines and the title of this pull request are the seat's write, not the author's (a dev writes a body once, at creation): the title takes
!and the body'sClause-②:line takesyes (narrowing). Until then the declaration of record for the contract review is the changeset's.Generated by Claude Code