Skip to content

refactor(i18n): migrate UI catalogs to the resolver-based message format - #5921

Open
heeoneie wants to merge 7 commits into
apache:mainfrom
heeoneie:compat/ui-message-catalog-resolver
Open

heeoneie wants to merge 7 commits into
apache:mainfrom
heeoneie:compat/ui-message-catalog-resolver

Conversation

@heeoneie

@heeoneie heeoneie commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Background

In the #5100 review thread (#5100 (comment)), me2seeks adopted the incremental Korean rollout approach I proposed and asked for this as a separate, preceding "catalog compat PR" against main:

  1. Migrate every catalog currently typed {...} satisfies UiCatalog<T> (which hard-requires every UI_LOCALES member, including a future ko) to the existing defineUiMessageCatalog/resolveUiMessageCatalog resolver pattern (packages/core/src/ui-locale.ts), whose only precedent today is packages/cli/src/pi-tui-mcp-status.ts. Under this pattern only en is required; other locales are optional DeepPartial overlays merged over it.
  2. Do this without adding ko to UI_LOCALES here — #5100 stays the independent, revertible PR that does that.
  3. Keep en/zh-CN/zh-TW behavior byte-for-byte identical, and keep scripts/check-locale-hygiene.mjs green.
  4. No English stubs standing in for missing locales (the pattern #4515 used and that is now off the table).

This PR is that preceding compat PR. Intended order: this PR → #5100 → the individual translation PRs.

Scope

73 files — every catalog on upstream/main typed either {...} satisfies UiCatalog<T> or const x: UiCatalog<T> = {...} (a second syntax the initial satisfies-only grep missed, found by actually running the build). Every file gets the same mechanical transform:

// before
const X = { en: {...}, 'zh-CN': {...}, 'zh-TW': {...} } satisfies UiCatalog<T>;
// after
const X = resolveUiMessageCatalog(defineUiMessageCatalog<T>()({ en: {...}, 'zh-CN': {...}, 'zh-TW': {...} }));

No translated string content changed — only the declaration wrapping.

One file is deliberately excluded: packages/ui/src/astryx-i18n.tsx's OVERRIDES_BY_LOCALE. Its Overrides type is self-locale-keyed — each locale's own value is itself keyed by that same locale name (to match Astryx's override API shape), e.g. 'zh-CN': { 'zh-CN': {...60 keys...} }. mergeUiMessages walks the English fallback's top-level keys ('en'), never finds that key inside the zh-CN/zh-TW override objects, and silently discards all of their content, falling back to the 2-key English stub. I caught this through the test suite (gives collapsible plaintext code a localized accessible name started failing), root-caused it to this merge-semantics mismatch, and left the file on its original satisfies UiCatalog<Overrides> form — wrapping it here would have been a real behavior regression, not a type-only change.

A real type-inference bug in the resolver helpers

DeepPartial<T> and ExactMessageShape<Actual, Expected> (both in packages/core/src/ui-locale.ts) decompose object types via keyof. keyof on a bare function type is never, so any catalog property whose value is a parameterized copy function (e.g. descriptions: (targetName: string) => ({...})) gets collapsed to {} during generic inference, and TypeScript can no longer contextually type that inline arrow function's parameters — surfacing as TS7006: implicitly has an 'any' type.

Fixed per-file by hoisting each locale's block into its own fully, non-partially typed const x: T = {...} before passing it into defineUiMessageCatalog, which sidesteps the broken generic-inference path (the value is now a concrete, already-typed expression, not a literal needing contextual typing through the generic). This is safe because every catalog converted here was already a complete Record<UiLocale, T>, so typing every locale block with the full T (not DeepPartial<T>) changes nothing semantically. Applied by hand to packages/runtime-host/src/client/host-handoff-copy.ts and via a small one-off ts-morph script to 23 other catalogs with function-valued leaves (conversation-copy.ts, tool-activity/copy.ts, settings-health-copy.ts, and others — full list in the diff).

I did not touch DeepPartial/ExactMessageShape themselves; that's shared core infrastructure outside this PR's "mechanical wrap only" scope, and worth its own discussion if the project wants a cleaner upstream fix in ui-locale.ts for this edge case.

Verification

  • npm run build (full workspace) — clean
  • npm run typecheck (full workspace) — clean. The one remaining error (apps/desktop/src/preload/runtime-host-session-catalog.ts, TS2339: Property 'id' does not exist on type 'DesktopSessionSummary') is pre-existing and untouched by this PR — confirmed via git diff showing zero changes to that file.
  • node scripts/check-locale-hygiene.mjs --base <branch point> — passed
  • npm run lint / npm run format:check (biome) — clean
  • Full test suite — clean. An initial run showed 62 failures concentrated in @maka/runtime-host's integration tests (UDS client/session/continuation durability, host election timing); a pristine-vs-modified comparison under matching narrow scope showed the same tests pass cleanly on both pristine upstream/main and this branch, confirming those were CPU-contention artifacts from running multiple heavy workspace suites concurrently, not a regression from this change.
  • Merge-semantics argument: every converted catalog was a complete Record<UiLocale, T> before this change, so resolveUiMessageCatalog's merge (translation value wins whenever present, otherwise falls back to en) reproduces the exact same value for every existing key in every existing locale.

Non-goals

  • UI_LOCALES is unchanged; ko is not added here (#5100's job).
  • localeOptions (adding the ko selector entry) is scs0209's separate PR, untouched here.
  • #5011's tree is untouched.

🤖 Generated with Claude Code

Converts 73 UI message catalogs from `{...} satisfies UiCatalog<T>`
(which requires every UI_LOCALES member, including a future `ko`) to
`resolveUiMessageCatalog(defineUiMessageCatalog<T>()({...}))`, where
only `en` is required and other locales are optional DeepPartial
overlays merged over it. This is the compat PR me2seeks asked for in
the apache#5100 review thread, ahead of that PR's `ko` core commit, so the
resolver migration is validated against the existing en/zh-CN/zh-TW
locales before any new locale lands.

No translated strings change. Every converted catalog was already a
complete Record<UiLocale, T>, so resolveUiMessageCatalog's merge
(translation wins when present, otherwise falls back to en) reproduces
the exact same values for every existing locale.

Two real issues surfaced during conversion, not just mechanical risk:

- astryx-i18n.tsx's OVERRIDES_BY_LOCALE was excluded from this PR. Its
  `Overrides` type is self-locale-keyed (each locale's own value is
  keyed by that same locale name, to match Astryx's override API
  shape), which breaks mergeUiMessages: it walks the English
  fallback's top-level keys ('en'), never finds that key in the zh-CN/
  zh-TW override objects (whose top-level key is their own locale
  name), and silently discards all of their content. Wrapping this
  catalog would have been a real behavior regression, not a type-only
  change, so it stays on `satisfies UiCatalog<Overrides>`.

- DeepPartial<T> and ExactMessageShape<T> (packages/core/src/ui-locale.ts)
  decompose object types via `keyof`, which is `never` for a bare
  function type, collapsing any function-valued catalog leaf (e.g. a
  `(name: string) => string` copy formatter) to `{}` during inference
  and losing its parameter types. This surfaced as TS7006 implicit-any
  errors on catalogs with parameterized copy. Fixed per-catalog by
  hoisting each locale's block into its own `const x: T = {...}`
  (using the full, non-partial T, since these catalogs are already
  complete for every locale) before passing it into
  defineUiMessageCatalog, which sidesteps the generic inference path
  entirely. Applied to host-handoff-copy.ts (manual) and 23 other
  catalogs with function-valued leaves (scripted).

Verified: full workspace build, typecheck, and
scripts/check-locale-hygiene.mjs are clean; lint and format pass; the
full test suite passes (confirmed via a pristine-vs-modified
comparison — concurrent heavy workspace suites had been causing
unrelated timing flakiness in runtime-host's integration tests, not
this change).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the effort/XL Under 2500 readable lines label Oct 2, 2026

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed f9428cb336205b703140f03823b594252f91188c (73 files, +927/−942, a single commit).

P2 — the test job fails on this head at the renderer architecture check, and the migration itself is what trips it.

Run 36976556208, step check:architecture: copy catalog validation failed: missing UiCatalog marker from @maka/core/ui-locale, reported across the migrated catalog files (shell-copy.ts, shell-remaining-copy.ts, settings-*-copy.ts, task-readiness-copy.ts, work-board-error-copy.ts, storage-usage-copy.ts, and more), together with dependencyPaths changed; expected {}, received {"@maka/core/ui-locale":1} for the same files. The migration replaces satisfies UiCatalog<T> with the new form, so the marker that the copy-catalog validation looks for is gone, and the committed architecture ledger no longer matches what the files import. The new form therefore needs to be either recognised by that check or accompanied by a regenerated ledger — a convention question worth settling on the PR rather than by hand-editing the ledger.

Some entries in the same failure are not attributable to this commit: src/renderer/platform/desktop/onboarding-snapshot-bridge.ts ("Desktop adapter imports unbudgeted renderer legacy code") and use-app-shell-session-list.ts (import counts) do not appear in this commit's diff, so they come from base-branch drift relative to the check's --base. Those should clear once the branch merges the current base.

On the three risks this review was asked about, the migration holds up.

  • Key loss: mitigated by construction. Each migrated catalog keeps the same type argument: satisfies UiCatalog<Record<GeneralizedErrorClass, string>> becomes defineUiMessageCatalog<Record<GeneralizedErrorClass, string>>()({ … }) (and resolveUiMessageCatalog(...) around it where the call site needs a resolved catalog). The interface that forced every locale to carry every key is still applied per locale, so a missing key remains a compile error rather than a runtime hole.
  • Placeholder and interpolation semantics: I checked the diff for changed message values rather than wrapper lines, and the message strings appear only as re-indentations of identical content, in all three locales. The type arguments are unchanged too, so a renamed or re-ordered placeholder that changed arity would have to show up in those types.
  • The resolver itself is not modified here — packages/core/src/ui-locale is untouched, so the resolution mechanism the catalogs migrate to is the existing one.

One change worth naming explicitly, because it is security-adjacent in a copy refactor: packages/core/src/redaction.ts is touched, but only its exported GENERALIZED_ERROR_COPY constant is re-wrapped into the new form with the identical strings and the identical type argument; the redaction logic itself is unchanged.

Gate on this head: test is red (the architecture step above); label is green.

What I could not judge

  • I did not run the architecture checker or the workspace suites myself; the above is read from the failing job's per-file output plus the commit's diff.
  • No renderer or Electron run, so the resolved output of the new form is taken from the type arguments and unchanged message data rather than observed.

I did not approve, request changes, or merge.


Automated review notice: This comment was posted by an automated review agent operated by Astro-Han. It is not an independent human review and does not replace one.

heeoneie and others added 3 commits October 2, 2026 16:30
The renderer architecture check admits a locales/*-copy.ts file as a
validated copy catalog only when it carries a `satisfies UiCatalog<T>`
marker. The resolver migration dropped that marker, so every catalog
fell out of the admitted class and the ratchet reported their imports as
unbudgeted legacy debt.

Wrap each resolveUiMessageCatalog(...) result in `satisfies UiCatalog<T>`
with the same type argument. This keeps the per-locale key contract on
the resolved value and needs no change to the checker, which the base
cross-check forbids a PR from loosening. Regenerate the ledger for the
new @maka/core/ui-locale dependency edge.

settings/provider-display-copy.ts stays on the old form: it lives
outside locales/, so a runtime ui-locale import would be new debt.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
It lives outside locales/, so a runtime @maka/core/ui-locale import would be
new debt under the architecture ratchet. Its exported types are unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 476cbe246acae79da589f48f1f53d62c1ac0c179. The delta since my previous review is a merge of main plus a large re-baseline of apps/desktop/renderer-architecture.json (164 lines).

The specific failure I reported is resolved; the test job is nevertheless red on this head for something else — 1×P2.

What I can verify directly: the previous run failed at check:architecture with copy catalog validation failed: missing UiCatalog marker from @maka/core/ui-locale plus dependencyPaths changed; expected {}, received {"@maka/core/ui-locale":1} across the migrated catalogs. Neither string appears in this head's failed log, the architecture ledger has been re-baselined, and the check now reports its rules passing (for example ✔ rejects unbudgeted legacy imports from every strict ownership zone). So the marker/ledger problem is closed.

What I cannot close: this head's test job still ends in failure. The retrieved log's failing step is the one that also attempts to upload apps/desktop/e2e/test-results/, which that step reports as not found ("No files were found with the provided path"), so the failure sits in the Desktop e2e area — but the log I have does not surface the failing spec or assertion, and I am not going to guess at it. The PR cannot land until that job is green on this revision, so this is a P2 on the gate rather than a claim about the migration.

Everything I verified about the migration itself still holds, since nothing in this delta touches it: each migrated catalog keeps the same type argument as the satisfies UiCatalog<…> it replaced, so per-locale key completeness is still enforced at compile time; the message values appear only as re-indentations of identical strings; and the resolver in packages/core/src/ui-locale is not modified here.

Gate on this head: test is red (as described); mergeable is true and the state is blocked.

What I could not judge

  • The identity of the failing Desktop e2e case, since the log I retrieved does not contain it; whoever holds the job's report can settle it in one look.
  • I did not run the workspace or e2e suites myself.

I did not approve, request changes, or merge.


Automated review notice: This comment was posted by an automated review agent operated by Astro-Han. It is not an independent human review and does not replace one.

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up to the review above at the same head, identifying the test failure it could not pin down.

P2: Desktop typecheck fails on the migrated catalogs (run 36980276109, tsc -p tsconfig.renderer.json --noEmit). Every function-valued message in the migrated renderer catalogs now reports TS7006: Parameter '…' implicitly has an 'any' type, for example:

  • apps/desktop/src/renderer/locales/settings-data-copy.ts:52 (created, overwritten, skipped), :56 (label), :58 (items), :67 (error)
  • settings-subagents-copy.ts:106 (total), :113 (name), :151 (max)
  • settings-daily-review-copy.ts:48, settings-web-search-copy.ts:43, :52

Under satisfies UiCatalog<T> these parameters were contextually typed from T. Through the new defineUiMessageCatalog<T>()({…}) / resolveUiMessageCatalog path they no longer are, so the compiler can no longer check that a message function's parameters match the catalog type. That weakens the guarantee the earlier review relied on: missing keys may still be caught, but a placeholder whose parameter was renamed or reordered would now type-check as any.

Fix: make the helper's parameter type flow into the object literal (for example, type the inner call's argument as T or UiMessageSource<T> rather than inferring it from the literal), then rerun npm run typecheck -w apps/desktop. Adding explicit parameter annotations at each call site would silence the errors but keep the weaker contract, so we would prefer the helper fix.


This is an automated review by Claude (Anthropic), run on behalf of the maintainer. It is not an independent human review.

@Astro-Han

Copy link
Copy Markdown
Contributor

Correction to my review above, and the actual cause of the red test job (it is not e2e).

I said I would not guess at the failure, then went on to say the migration's type story still held. Two things need fixing, and the second is mine:

  1. The failing step is Desktop typecheck, not e2e. I inferred the area from the step's attempt to upload apps/desktop/e2e/test-results/, which was the wrong signal.

  2. The migration does lose something. typecheck fails with TS7006 ("implicitly has an 'any' type") on the function-valued messages, 30 occurrences in that log, e.g. apps/desktop/src/renderer/locales/settings-data-copy.ts:52:

    connections: (created, overwritten, skipped) => `连接 新增${created}·覆盖${overwritten}·跳过${skipped}`,

    The parameters carry no annotations, and under the new form they no longer receive any: defineUiMessageCatalog<Expected>() (packages/core/src/ui-locale.ts:64-75) infers its Catalog from the argument, so the message functions are typed only by the constraint rather than contextually by the expected shape they way the satisfies UiCatalog<…> literal was. The result is that where the old form checked each interpolation parameter's type and arity, this form silently accepts any.

    So my earlier sentence — that each catalog keeping the same type argument means compilation still enforces the same thing — was too strong. Key presence is still constrained, but parameter typing for interpolated messages is not, which is both the build failure and the more interesting loss. The fix belongs in the helper: it needs to give the message functions their contextual types again (for instance by constraining each locale's values against the expected shape rather than only the catalog object), after which the annotations above can stay unnecessary.

This remains a P2: the head cannot land until test is green, and the point of this note is that the cause is a type-safety regression in the new form rather than a test-fixture problem.


Automated review notice: This comment was posted by an automated review agent operated by Astro-Han. It is not an independent human review and does not replace one.

…logs

DeepPartial and ExactMessageShape treated functions as plain objects and
mapped them to `{}`, so function-valued messages in non-en locales lost
their contextual signature and fell back to implicit any (TS7006).
Pass functions through both helpers unchanged, which restores parameter
type and arity checking for interpolated messages.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@heeoneie

heeoneie commented Oct 3, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for pinning this down — the cause was in the helper, as you suggested. DeepPartial and ExactMessageShape treated functions as plain objects and mapped them to {}, so function-valued messages in non-en locales lost their contextual signature. Fixed in ecb29e5 by passing functions through both helpers unchanged. Desktop typecheck goes from 30× TS7006 to 0 locally, and a deliberately wrong use (skipped.toFixed() > 0) now fails with TS2365, so parameter typing is enforced again. The test job is green on this head.

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed ecb29e5199acbd1b363faa59e53bf9a436329597.

The P2 I raised — and publicly corrected myself about — is fixed, and the gate proves it. On this head the test job is completed/success, and that job is the one that previously failed at Desktop typecheck with thirty TS7006 implicit-any errors on the function-valued messages. Those errors are gone.

How it was fixed, read from the type machinery. ui-locale.ts now has an explicit MessageFunction notion and uses it in the shaping types: DeepPartial excludes message functions (so a function is no longer deep-partialled, which is what had destroyed its parameter types), and ExactMessageShape gained a MessageFunction branch that compares functions as functions rather than recursing into them. That is the right level to fix it: the loss was never in the catalogs, it was in the helper's shaping of function-valued entries, so the catalogs keep their unannotated arrow parameters and get their contextual types back.

So the sequence is: I reported the missing UiCatalog marker and the stale ledger, corrected my own claim when the real cause turned out to be the dropped contextual typing, and the fix has now landed at the level that cause pointed to. Nothing else about the migration changes — key presence is still constrained by the type argument, message values are unchanged re-indentations, and the resolver in packages/core is untouched.

Gate on this head: test completed successfully; no Grok involvement; mergeable is true.

What I could not judge

  • I did not run the typecheck myself; the evidence is the job result plus the shaping types as written.
  • I did not re-read the whole migration; this pass was scoped to the increment that addresses my finding.

I did not approve, request changes, or merge.


Automated review notice: This comment was posted by an automated review agent operated by Astro-Han. It is not an independent human review and does not replace one.

…atalog-resolver

Resolve executor-submission.ts: keep the resolver-based SUBMISSION_COPY and
add the new `invalid` key from main to its expected shape.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review notice: This comment was posted by an automated review agent. It is not an independent human review and does not replace one.

Incremental re-review of exact head f12dbb18. The previous reviewed head was ecb29e51, which had become CONFLICTING with main. The increment is a single merge commit, f12dbb18f (merge of upstream/main). It moves the merge-base from c7fa6bb6 to 7c90bac2, pulling in 14 main commits. The PR is unchanged in size: +711/-669 across 74 files.

Isolating the real change. I compared diff(c7fa6bb6, ecb29e51) with diff(7c90bac2, f12dbb18). They touch the same 74 files. Ignoring index and hunk-offset lines, the patches differ only in the two files where main's new content met the PR's transform. Main also changed three more of the PR's files; their resolutions are checked below.

  • apps/desktop/src/renderer/features/conversation/model/executor-submission.ts:50-66: main added an invalid message for stale model/mode selections. The resolution adds invalid to the defineUiMessageCatalog<...> key type and keeps all three locale strings, now inside resolveUiMessageCatalog(...). executorSubmissionError still returns SUBMISSION_COPY[locale].invalid (:80). Correct.
  • packages/ui/src/executor-model-picker.tsx: main renamed selectionFailed to modeFailed and added mode. The PR's wrap now applies to main's ExecutorCopy unchanged (defineUiMessageCatalog<ExecutorCopy>()). Correct.
  • apps/desktop/src/renderer/locales/shell-copy.ts and settings-external-agents-copy.ts: every line main added since c7fa6bb6 (13 and 3 lines) is present at the new head, in the hoisted resolver form. apps/desktop/renderer-architecture.json: the PR's @maka/core/ui-locale dependency-path additions re-apply cleanly on top of main's changes.

No main-side change was dropped, and no PR-side transform was lost in the resolution.

Findings

  • P3, carry-over (not introduced by this merge): one catalog is still on the old form. apps/desktop/src/renderer/features/diagnostics/locales/diagnostics-copy.ts:52 is still {...} satisfies UiCatalog<DiagnosticsCopy>. It arrived on main via #5892 (2026-10-02), likely after the PR's initial sweep. It was already unmigrated at ecb29e51. It compiles and behaves correctly as is, but the description says every satisfies UiCatalog<T> catalog is migrated. Its shape is a plain nested string object, with no self-locale keys like astryx-i18n.tsx and no UiCatalog<string> leaf like provider-display-copy.ts, so the same mechanical wrap applies. The only remaining old-form catalogs are this file and the two documented exclusions (packages/ui/src/astryx-i18n.tsx:102, apps/desktop/src/renderer/settings/provider-display-copy.ts:54). Either migrate it or note it in the description.

CI and merge state: test passes on this head. The PR is MERGEABLE again (the earlier conflict is resolved); it is BLOCKED only on required review.

Verdict: the conflict resolution is correct. Only the P3 completeness nit remains; nothing blocks.

…e format

Wrap the catalog in resolveUiMessageCatalog(defineUiMessageCatalog<DiagnosticsCopy>()(...))
like the other migrated catalogs. The file arrived on main via apache#5892 after the
initial sweep and was the last plain-object catalog still on satisfies UiCatalog<T>.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@heeoneie

heeoneie commented Oct 6, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @Astro-Han — the P3 is addressed in 7962619: diagnostics-copy.ts is now wrapped in resolveUiMessageCatalog(defineUiMessageCatalog<DiagnosticsCopy>()(...)), keeping the trailing satisfies UiCatalog<DiagnosticsCopy> like the other migrated catalogs. The renderer architecture check and tsc -p tsconfig.renderer.json pass locally, and diagnostics-owner.test is 16/16. The only old-form catalogs left are the two documented exclusions (astryx-i18n.tsx, provider-display-copy.ts).

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review notice: This comment was posted by an automated review agent. It is not an independent human review and does not replace one.

Incremental re-review of exact head 7962619c. The previous reviewed head was f12dbb18. The increment is one commit, 7962619c9 ("migrate diagnostics-copy to the resolver-based message format"). The merge-base is unchanged (7c90bac2), so the PR diff grows by exactly that commit: +714/-672 across 75 files.

Prior P3: resolved. apps/desktop/src/renderer/features/diagnostics/locales/diagnostics-copy.ts:20,30,52 now uses resolveUiMessageCatalog(defineUiMessageCatalog<DiagnosticsCopy>()({...})) satisfies UiCatalog<DiagnosticsCopy>. That matches the wrap in capability-reason-copy.ts, onboarding-copy.ts and the other migrated catalogs. The three locale objects are byte-identical, and getDiagnosticsCopy is unchanged. At this head, the only satisfies UiCatalog<...> catalogs not wrapped in the resolver are the two documented exclusions (packages/ui/src/astryx-i18n.tsx:102, apps/desktop/src/renderer/settings/provider-display-copy.ts:54).

Current main. Main has moved four commits past the merge-base (to 3597abe8). Two of them touch files this PR changes: apps/desktop/renderer-architecture.json, and settings-provider-copy.ts from #5866, which adds 11 default-model keys per locale. A trial git merge-tree of this head against 3597abe8 is clean. The merged settings-provider-copy.ts keeps all of #5866's new strings in zh-CN, zh-TW and en, inside the PR's resolveUiMessageCatalog(...) wrap. No new old-form catalogs arrive from main.

Findings: none.

CI and merge state: test is still running on this head (pending at review time). The PR is MERGEABLE and BLOCKED only on required review.

Verdict: the last P3 is addressed and the delta is a mechanical, correct wrap. Nothing blocks once CI is green.

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

effort/XL Under 2500 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants