From ec99f174784dc9248533308d834dc5f4ca18a78f Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Sat, 18 Jul 2026 04:54:34 -0700 Subject: [PATCH] fix: i18n ephemeral policy keys + planning loading race (#2298) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Full Suite [29642536622](https://github.com/Runfusion/Fusion/actions/runs/29642536622) after #2297: shards 1–2 green; shard 4 i18n parity + shard 3 PlanningMode loading. - Add `settings.general.ephemeralAgentTaskCreationPolicy*` to zh-CN/zh-TW/fr/es/ko (FN-8265 keys were en-only). - Gate the follow-up SSE question in the PlanningMode loading test so `.planning-loading` is assertable before the next question arrives (CI race). ## Test plan - [x] `@fusion/i18n` parity + gate-coverage tests - [x] PlanningModeModal loading-after-response test - [ ] Full Suite all 4 shards green on main ## Summary by CodeRabbit * **New Features** * Added localized settings text for configuring ephemeral agent follow-up task creation policies in Spanish, French, Korean, Simplified Chinese, and Traditional Chinese. * Added options to allow, require validation, or deny follow-up task creation. * **Tests** * Improved planning mode loading-state test reliability with deterministic synchronization. --- ...PlanningModeModal.ui-interactions.test.tsx | 22 +++++++++++++++---- packages/i18n/locales/es/app.json | 7 +++++- packages/i18n/locales/fr/app.json | 7 +++++- packages/i18n/locales/ko/app.json | 7 +++++- packages/i18n/locales/zh-CN/app.json | 7 +++++- packages/i18n/locales/zh-TW/app.json | 7 +++++- 6 files changed, 48 insertions(+), 9 deletions(-) diff --git a/packages/dashboard/app/components/__tests__/PlanningModeModal.ui-interactions.test.tsx b/packages/dashboard/app/components/__tests__/PlanningModeModal.ui-interactions.test.tsx index 8c05a4846f..5c6dc25809 100644 --- a/packages/dashboard/app/components/__tests__/PlanningModeModal.ui-interactions.test.tsx +++ b/packages/dashboard/app/components/__tests__/PlanningModeModal.ui-interactions.test.tsx @@ -517,6 +517,12 @@ describe("PlanningModeModal", () => { }); it("shows loading state with appropriate text after submitting a response", async () => { + /* + FNXC:DashboardTests 2026-07-18-11:45: + Full Suite shard 3 failed when a 50ms setTimeout second-question race skipped the + loading paint under CI load. Gate the next SSE question on an explicit release so + `.planning-loading` is assertable before the follow-up question arrives. + */ const secondQuestion: PlanningQuestion = { id: "q-requirements", type: "text", @@ -525,6 +531,10 @@ describe("PlanningModeModal", () => { }; let streamHandlers: any = null; + let releaseSecondQuestion: (() => void) | null = null; + const secondQuestionGate = new Promise((resolve) => { + releaseSecondQuestion = resolve; + }); mockConnectPlanningStream.mockImplementation((_sessionId: string, _projectId: string | undefined, handlers: any) => { streamHandlers = handlers; @@ -540,12 +550,12 @@ describe("PlanningModeModal", () => { }); mockRespondToPlanning.mockImplementation(async () => { - // Simulate server broadcasting second question via the existing SSE connection - setTimeout(() => { + // Hold the follow-up question until the loading UI has been observed. + void secondQuestionGate.then(() => { if (streamHandlers) { streamHandlers.onQuestion?.(secondQuestion); } - }, 50); + }); return { sessionId: "session-123", currentQuestion: null, summary: null }; }); @@ -581,10 +591,14 @@ describe("PlanningModeModal", () => { // Verify thinking container is visible during loading expect(container.querySelector(".planning-thinking-container")).not.toBeNull(); + await act(async () => { + releaseSecondQuestion?.(); + }); + // Wait for second question to appear await waitFor(() => { expect(screen.getByText("What are the key requirements?")).toBeDefined(); - }, { timeout: 3000 }); + }); }); }); diff --git a/packages/i18n/locales/es/app.json b/packages/i18n/locales/es/app.json index d427fb85e1..f570e599eb 100644 --- a/packages/i18n/locales/es/app.json +++ b/packages/i18n/locales/es/app.json @@ -5897,7 +5897,12 @@ "selectNavItem": "Elegir un destino", "removeNavItem": "Quitar {{item}}", "moveNavItemEarlier": "Mover {{item}} antes", - "moveNavItemLater": "Mover {{item}} después" + "moveNavItemLater": "Mover {{item}} después", + "ephemeralAgentTaskCreationPolicy": "Tareas de seguimiento de agentes efímeros", + "ephemeralAgentTaskCreationPolicyHint": "Sin valor predeterminado — si no se define, se usa Permitir. «Tras validación» envía una propuesta a tu buzón para aprobar con un clic; Denegar rechaza la creación de tareas de seguimiento.", + "ephemeralAgentTaskCreationPolicyAllow": "Permitir", + "ephemeralAgentTaskCreationPolicyUponValidation": "Tras validación", + "ephemeralAgentTaskCreationPolicyDeny": "Denegar" }, "globalGeneral": { "andShowsUpdateNoticesInTheCLIAnd": "", diff --git a/packages/i18n/locales/fr/app.json b/packages/i18n/locales/fr/app.json index c25bd90316..f97df9f4ce 100644 --- a/packages/i18n/locales/fr/app.json +++ b/packages/i18n/locales/fr/app.json @@ -5897,7 +5897,12 @@ "selectNavItem": "Choisir une destination", "removeNavItem": "Retirer {{item}}", "moveNavItemEarlier": "Déplacer {{item}} plus tôt", - "moveNavItemLater": "Déplacer {{item}} plus tard" + "moveNavItemLater": "Déplacer {{item}} plus tard", + "ephemeralAgentTaskCreationPolicy": "Tâches de suivi des agents éphémères", + "ephemeralAgentTaskCreationPolicyHint": "Pas de valeur par défaut — non défini revient à Autoriser. « Sur validation » envoie une proposition à votre boîte pour une approbation en un clic ; Refuser bloque la création de tâches de suivi.", + "ephemeralAgentTaskCreationPolicyAllow": "Autoriser", + "ephemeralAgentTaskCreationPolicyUponValidation": "Sur validation", + "ephemeralAgentTaskCreationPolicyDeny": "Refuser" }, "globalGeneral": { "andShowsUpdateNoticesInTheCLIAnd": "", diff --git a/packages/i18n/locales/ko/app.json b/packages/i18n/locales/ko/app.json index 476441c169..b4ceda94c8 100644 --- a/packages/i18n/locales/ko/app.json +++ b/packages/i18n/locales/ko/app.json @@ -5897,7 +5897,12 @@ "selectNavItem": "대상 선택", "removeNavItem": "{{item}} 제거", "moveNavItemEarlier": "{{item}} 앞으로 이동", - "moveNavItemLater": "{{item}} 뒤로 이동" + "moveNavItemLater": "{{item}} 뒤로 이동", + "ephemeralAgentTaskCreationPolicy": "임시 에이전트 후속 작업", + "ephemeralAgentTaskCreationPolicyHint": "기본값 없음 — 미설정 시 허용으로 폴백합니다. 검증 후는 받은편지함으로 제안을 보내 원클릭 승인합니다. 거부는 후속 작업 생성을 거부합니다.", + "ephemeralAgentTaskCreationPolicyAllow": "허용", + "ephemeralAgentTaskCreationPolicyUponValidation": "검증 후", + "ephemeralAgentTaskCreationPolicyDeny": "거부" }, "globalGeneral": { "andShowsUpdateNoticesInTheCLIAnd": "", diff --git a/packages/i18n/locales/zh-CN/app.json b/packages/i18n/locales/zh-CN/app.json index e4e4de4104..d4e045ba8e 100644 --- a/packages/i18n/locales/zh-CN/app.json +++ b/packages/i18n/locales/zh-CN/app.json @@ -5897,7 +5897,12 @@ "selectNavItem": "选择目标", "removeNavItem": "移除 {{item}}", "moveNavItemEarlier": "将 {{item}} 前移", - "moveNavItemLater": "将 {{item}} 后移" + "moveNavItemLater": "将 {{item}} 后移", + "ephemeralAgentTaskCreationPolicy": "临时代理后续任务", + "ephemeralAgentTaskCreationPolicyHint": "无默认值 — 未设置时回退为允许。需验证会将提案发送到邮箱供一键批准;拒绝则禁止创建后续任务。", + "ephemeralAgentTaskCreationPolicyAllow": "允许", + "ephemeralAgentTaskCreationPolicyUponValidation": "需验证", + "ephemeralAgentTaskCreationPolicyDeny": "拒绝" }, "globalGeneral": { "andShowsUpdateNoticesInTheCLIAnd": "", diff --git a/packages/i18n/locales/zh-TW/app.json b/packages/i18n/locales/zh-TW/app.json index 173a2b2e7b..261cff5d5d 100644 --- a/packages/i18n/locales/zh-TW/app.json +++ b/packages/i18n/locales/zh-TW/app.json @@ -5897,7 +5897,12 @@ "selectNavItem": "選擇目的地", "removeNavItem": "移除 {{item}}", "moveNavItemEarlier": "將 {{item}} 前移", - "moveNavItemLater": "將 {{item}} 後移" + "moveNavItemLater": "將 {{item}} 後移", + "ephemeralAgentTaskCreationPolicy": "臨時代理後續任務", + "ephemeralAgentTaskCreationPolicyHint": "無預設值 — 未設定時回退為允許。需驗證會將提案傳送到信箱供一鍵核准;拒絕則禁止建立後續任務。", + "ephemeralAgentTaskCreationPolicyAllow": "允許", + "ephemeralAgentTaskCreationPolicyUponValidation": "需驗證", + "ephemeralAgentTaskCreationPolicyDeny": "拒絕" }, "globalGeneral": { "andShowsUpdateNoticesInTheCLIAnd": "",