You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(i18n): the change card's Confirm button sent text the cloud gate rejects, + parity ratchet (#2905)
* fix(i18n): the change card's Confirm button sent text the cloud gate rejects
The English `console.ai.changesConfirmMessage` was "Confirm the changes —
apply what you just proposed." The cloud confirm gate (service-ai-studio
confirm-gate.ts APPROVAL_RE) recognises `apply (this|the) change` — not
"apply what". The message therefore failed the gate, and failing it is
silent: the agent re-proposes instead of applying and the Confirm button on
the change card just looks inert.
This hit English conversations AND all eight locales that fall back to
English for that key. Now "Confirm — apply the change you just proposed." —
singular, so it still matches if the gate ever tightens to a word boundary.
The Chinese string always matched (确认修改) and is unchanged. The same
literal lives in four places (locale pack, prop default, its doc comment,
AiChatPage defaultValue); all four move together.
Why the existing guard missed it: i18n.test.ts mirrored only the CHINESE
clause of APPROVAL_RE, and the English half was weakened to "starts with
Confirm, contains apply" because nothing in this repo could see the real
pattern. That assertion passed against a string the gate rejected — green
guard, broken feature.
The mirror is now verbatim and covers both clauses, driving an it.each over
every outbound approval message in zh and en. Two supporting tests keep it
honest: the gate must stay narrow (a plain build request is not approval),
and planAnswerMessage must NOT match, since it answers a structure question
rather than granting blanket approval.
Verified by running the real regex against every outbound string, and by
mutation-testing the new guard against the exact string that shipped.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TubWYdWquVkS9dj733sDmC
* test(i18n): ratchet the four backfilled namespaces so they cannot silently erode
#2903 translated `console`, `home`, `topbar` and `layout` into all ten packs.
Nothing stopped that from decaying: `fallbackLng: 'en'` means dropping a key
from `de` renders English, which reads as "not translated yet" rather than
"we lost this", and the missing-key handler is dev-only so CI never sees it.
This is #2872's P3 applied only to the namespaces that are actually complete.
Full parity would fail today by ~277 keys per pack with no action attached —
a broken build, not a guard. Widen RATCHETED_NAMESPACES as each remaining
namespace is translated, not before.
Asserts both directions, because the packs have drifted both ways before:
every ratcheted en key exists in the other nine packs, and no pack defines a
ratcheted key en lacks (#2872 part b was exactly that, 74 keys deep, hidden
behind a component-private fallback so English "happened to" render).
The four outbound agent messages are excluded — they are deliberately absent
from the eight non-gate packs, and outbound-agent-messages.test.ts owns that
invariant; the two guards would otherwise contradict each other.
A non-vacuity assertion pins the ratchet at >300 keys and requires every named
namespace to contribute, so a rename cannot reduce the file to a no-op.
Mutation-tested in both directions: removing topbar.offline from fr fails the
first, adding an en-less key to fr fails the second.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TubWYdWquVkS9dj733sDmC
---------
Co-authored-by: Claude <noreply@anthropic.com>
0 commit comments