diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index dcdec079..fe4b2244 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -5436,6 +5436,42 @@ "state": "只讀鏡像 / 先觀測", "detail": "只顯示態勢與缺口;掃描、修復、更新、阻擋仍未開閘。" }, + "executiveSnapshot": { + "eyebrow": "高層快照", + "title": "不用讀完整頁,也能先看懂目前資安工作", + "subtitle": "把已完成可見成果、納管範圍、下一個卡點與禁止動作壓成第一屏摘要;下方再提供拓樸、決策跑道與證據細節。", + "axes": { + "framework": { + "label": "框架 / 治理 / UI" + }, + "evidence": { + "label": "S4.9 owner evidence" + }, + "runtime": { + "label": "執行期開閘" + } + }, + "cards": { + "visibleWork": { + "title": "已完成可見工作", + "body": "IwoooS 已把焦點導覽、視覺資安網、拓樸圖、決策跑道、Gate 雷達、AwoooP 與 VibeWork 納管呈現到前台。" + }, + "assetMesh": { + "title": "資產與主機已納管", + "body": "8 類產品 / 網站 / 工具面與 112、111、168 三台主機已放入同一個只讀資安網。" + }, + "nextBlocker": { + "title": "下一個真正卡點", + "body": "整體百分比要前進,需要 S4.9 負責人回覆、脫敏證據與 reviewer acceptance。" + }, + "runtimeLock": { + "title": "仍禁止執行", + "body": "掃描、SSH、主機更新、修復、部署與 source-control mutation 都維持 Gate 0。" + } + }, + "boundaryTitle": "高層快照邊界", + "boundaryIntro": "這個快照只做管理層摘要與導覽,不是掃描器、主機更新器、部署器或版本來源切換器。" + }, "focusDeck": { "eyebrow": "首層焦點導覽", "title": "先選一條線,不用讀完整頁", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index dcdec079..fe4b2244 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -5436,6 +5436,42 @@ "state": "只讀鏡像 / 先觀測", "detail": "只顯示態勢與缺口;掃描、修復、更新、阻擋仍未開閘。" }, + "executiveSnapshot": { + "eyebrow": "高層快照", + "title": "不用讀完整頁,也能先看懂目前資安工作", + "subtitle": "把已完成可見成果、納管範圍、下一個卡點與禁止動作壓成第一屏摘要;下方再提供拓樸、決策跑道與證據細節。", + "axes": { + "framework": { + "label": "框架 / 治理 / UI" + }, + "evidence": { + "label": "S4.9 owner evidence" + }, + "runtime": { + "label": "執行期開閘" + } + }, + "cards": { + "visibleWork": { + "title": "已完成可見工作", + "body": "IwoooS 已把焦點導覽、視覺資安網、拓樸圖、決策跑道、Gate 雷達、AwoooP 與 VibeWork 納管呈現到前台。" + }, + "assetMesh": { + "title": "資產與主機已納管", + "body": "8 類產品 / 網站 / 工具面與 112、111、168 三台主機已放入同一個只讀資安網。" + }, + "nextBlocker": { + "title": "下一個真正卡點", + "body": "整體百分比要前進,需要 S4.9 負責人回覆、脫敏證據與 reviewer acceptance。" + }, + "runtimeLock": { + "title": "仍禁止執行", + "body": "掃描、SSH、主機更新、修復、部署與 source-control mutation 都維持 Gate 0。" + } + }, + "boundaryTitle": "高層快照邊界", + "boundaryIntro": "這個快照只做管理層摘要與導覽,不是掃描器、主機更新器、部署器或版本來源切換器。" + }, "focusDeck": { "eyebrow": "首層焦點導覽", "title": "先選一條線,不用讀完整頁", diff --git a/apps/web/src/app/[locale]/iwooos/page.tsx b/apps/web/src/app/[locale]/iwooos/page.tsx index aa51adb2..530c5df6 100644 --- a/apps/web/src/app/[locale]/iwooos/page.tsx +++ b/apps/web/src/app/[locale]/iwooos/page.tsx @@ -189,6 +189,21 @@ type IwoooSFocusDeckItem = { tone: 'steady' | 'warn' | 'locked' } +type IwoooSExecutiveSnapshotCard = { + key: string + metric: string + icon: typeof ShieldCheck + tone: 'steady' | 'warn' | 'locked' +} + +type IwoooSExecutiveSnapshotAxis = { + key: string + value: string + percent: number + icon: typeof ShieldCheck + tone: 'steady' | 'warn' | 'locked' +} + type IwoooSImmediateVisualMeshNode = { key: string metric: string @@ -2419,6 +2434,38 @@ const iwooosCommandMapBoundaries = [ 'not_authorization=true', ] as const +const iwooosExecutiveSnapshotCards: IwoooSExecutiveSnapshotCard[] = [ + { key: 'visibleWork', metric: '7區', icon: CheckCircle2, tone: 'steady' }, + { key: 'assetMesh', metric: '8+3', icon: Network, tone: 'steady' }, + { key: 'nextBlocker', metric: 'S4.9', icon: FileWarning, tone: 'warn' }, + { key: 'runtimeLock', metric: 'Gate 0', icon: Lock, tone: 'locked' }, +] + +const iwooosExecutiveSnapshotAxes: IwoooSExecutiveSnapshotAxis[] = [ + { key: 'framework', value: '88%', percent: 88, icon: ShieldCheck, tone: 'steady' }, + { key: 'evidence', value: '0/4', percent: 0, icon: ClipboardCheck, tone: 'warn' }, + { key: 'runtime', value: '0', percent: 0, icon: Lock, tone: 'locked' }, +] + +const iwooosExecutiveSnapshotBoundaries = [ + 'iwooos_executive_snapshot_first_layer=true', + 'iwooos_executive_snapshot_card_count=4', + 'iwooos_executive_snapshot_axis_count=3', + 'iwooos_executive_snapshot_above_focus_deck=true', + 'iwooos_executive_snapshot_explains_done_next_blocked=true', + 'iwooos_executive_snapshot_execution_action_buttons_allowed=false', + 'iwooos_executive_snapshot_runtime_gate_count=0', + 'iwooos_executive_snapshot_owner_response_received_count=0', + 'iwooos_executive_snapshot_owner_response_accepted_count=0', + 'iwooos_executive_snapshot_scan_authorized=false', + 'iwooos_executive_snapshot_host_change_authorized=false', + 'iwooos_executive_snapshot_source_control_mutation_authorized=false', + 'runtime_execution_authorized=false', + 'active_runtime_gate_count=0', + 'action_buttons_allowed=false', + 'not_authorization=true', +] as const + const iwooosFocusDeckItems: IwoooSFocusDeckItem[] = [ { key: 'workMap', href: '#iwooos-command-map-board', metric: '6', icon: Radar, tone: 'warn' }, { key: 'unlockPath', href: '#iwooos-first-progress-unlock-path-board', metric: 'S4.9', icon: ListChecks, tone: 'warn' }, @@ -9535,6 +9582,164 @@ function IwoooSFirstProgressUnlockPathBoard() { ) } +function IwoooSExecutiveSnapshotBoard() { + const t = useTranslations('iwooos.executiveSnapshot') + const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const } + + return ( +
+
+
+
+
+ + {t('eyebrow')} +
+
+

{t('title')}

+

+ {t('subtitle')} +

+
+ +
+ {iwooosExecutiveSnapshotAxes.map(item => { + const Icon = item.icon + return ( +
+
+ + {item.value} +
+
+ {t(`axes.${item.key}.label` as never)} +
+
+
+
+
+ ) + })} +
+
+ +
+ {iwooosExecutiveSnapshotCards.map(item => { + const Icon = item.icon + return ( +
+
+ + {item.metric} +
+
+ {t(`cards.${item.key}.title` as never)} +
+
+ {t(`cards.${item.key}.body` as never)} +
+
+ ) + })} +
+
+ +
+ + {t('boundaryTitle')} + +

+ {t('boundaryIntro')} +

+
+ {iwooosExecutiveSnapshotBoundaries.map(item => ( + + {item} + + ))} +
+
+
+
+ ) +} + function IwoooSFocusDeckBoard() { const t = useTranslations('iwooos.focusDeck') const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const } @@ -16178,6 +16383,7 @@ export default function IwoooSPage({ params }: { params: { locale: string } }) { + diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 965908ec..2fa1af57 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,31 @@ +## 2026-06-01|IwoooS 高層快照層 + +**背景**: + +- 使用者持續指出 IwoooS 不能只是長文字與很多區塊,必須讓使用者更快理解目前到底完成哪些工作、哪些主機 / 產品已納管、下一個真正卡點與禁止動作。 +- 本輪不新增掃描、不更新 Kali / 開發主機、不切換 GitHub / Gitea、不提高初期資安限制;只把已存在的工作狀態壓成第一屏管理層快照。 + +**本次調整**: + +- `apps/web/src/app/[locale]/iwooos/page.tsx`: + - 新增 `IwoooSExecutiveSnapshotBoard`,放在頁首後、焦點導覽前。 + - 以四張摘要卡呈現:已完成可見工作、資產與主機已納管、下一個真正卡點、仍禁止執行。 + - 以三條狀態軸呈現:框架 / 治理 / UI、S4.9 owner evidence、執行期開閘。 + - 明確標示 `Gate 0`、`owner response=0`、`runtime_execution_authorized=false`,避免把 UI 可視化誤讀成執行授權。 +- `apps/web/messages/zh-TW.json` / `apps/web/messages/en.json`: + - 新增 `iwooos.executiveSnapshot` 全繁中文案;`en.json` 維持繁中鏡像。 +- `docs/security/iwooos-posture-projection.snapshot.json` / `security-mirror-status-rollup.snapshot.json`: + - 新增 `executive_snapshot_*` summary、cards、axes。 + - 新增 `S2.153` 進度 ledger;headline 不增加,因為這是 framework / UX / 可理解度提升,不是 runtime gate。 +- `scripts/security/security-mirror-progress-guard.py`: + - 新增高層快照的 component、testid、i18n、snapshot 與 runtime false 邊界 guard。 + +**進度邊界**: + +- 整體維持 `61%`;這輪提升的是「第一眼理解度」與「高層狀態摘要」。 +- `active_runtime_gate_count=0`、`runtime_execution_authorized=false`。 +- 未執行 Kali 掃描、未 SSH 變更 112 / 111 / 168、未開啟自動修復、未做 GitHub primary / Gitea 切換。 + ## 2026-06-01|AI 自健診 W-1 SLO:auto_execute_success_rate 修復 **背景**: diff --git a/docs/security/iwooos-posture-projection.snapshot.json b/docs/security/iwooos-posture-projection.snapshot.json index 500374f2..649223ce 100644 --- a/docs/security/iwooos-posture-projection.snapshot.json +++ b/docs/security/iwooos-posture-projection.snapshot.json @@ -83,6 +83,14 @@ "github_primary_ready_count": 0, "source_control_primary_readiness_item_count": 6, "action_buttons_allowed": false, + "executive_snapshot_first_layer": true, + "executive_snapshot_card_count": 4, + "executive_snapshot_axis_count": 3, + "executive_snapshot_above_focus_deck": true, + "executive_snapshot_explains_done_next_blocked": true, + "executive_snapshot_runtime_gate_count": 0, + "executive_snapshot_owner_response_received_count": 0, + "executive_snapshot_owner_response_accepted_count": 0, "all_product_coverage_snapshot_scope_count": 7, "all_product_coverage_snapshot_read_only_count": 7, "all_product_coverage_snapshot_runtime_ready_count": 0, @@ -186,6 +194,90 @@ "headline_status": "reviewed_after_awooop_read_only_production_landing_evidence", "not_authorization": true }, + "executive_snapshot_cards": [ + { + "card_id": "visibleWork", + "display_order": 1, + "display_mode": "first_screen_executive_snapshot", + "metric": "7區", + "summarizes_completed_visible_work": true, + "execution_action_button_allowed": false, + "runtime_gate_opened": false, + "runtime_execution_authorized": false, + "not_authorization": true + }, + { + "card_id": "assetMesh", + "display_order": 2, + "display_mode": "first_screen_executive_snapshot", + "metric": "8+3", + "summarizes_asset_and_host_scope": true, + "execution_action_button_allowed": false, + "runtime_gate_opened": false, + "runtime_execution_authorized": false, + "not_authorization": true + }, + { + "card_id": "nextBlocker", + "display_order": 3, + "display_mode": "first_screen_executive_snapshot", + "metric": "S4.9", + "summarizes_next_blocker": true, + "owner_response_received_count": 0, + "owner_response_accepted_count": 0, + "execution_action_button_allowed": false, + "runtime_gate_opened": false, + "runtime_execution_authorized": false, + "not_authorization": true + }, + { + "card_id": "runtimeLock", + "display_order": 4, + "display_mode": "first_screen_executive_snapshot", + "metric": "Gate 0", + "summarizes_forbidden_runtime_actions": true, + "execution_action_button_allowed": false, + "scan_authorized": false, + "host_change_authorized": false, + "source_control_mutation_authorized": false, + "runtime_gate_opened": false, + "runtime_execution_authorized": false, + "not_authorization": true + } + ], + "executive_snapshot_axes": [ + { + "axis_id": "framework", + "display_order": 1, + "display_mode": "first_screen_executive_snapshot_axis", + "value": "88%", + "percent": 88, + "runtime_delta": false, + "not_authorization": true + }, + { + "axis_id": "evidence", + "display_order": 2, + "display_mode": "first_screen_executive_snapshot_axis", + "value": "0/4", + "percent": 0, + "owner_response_received_count": 0, + "owner_response_accepted_count": 0, + "runtime_delta": false, + "not_authorization": true + }, + { + "axis_id": "runtime", + "display_order": 3, + "display_mode": "first_screen_executive_snapshot_axis", + "value": "0", + "percent": 0, + "runtime_gate_count": 0, + "runtime_execution_authorized": false, + "runtime_delta": false, + "not_authorization": true + } + ], "first_progress_unlock_path_steps": [ { "step_id": "owner_response_scope", diff --git a/docs/security/security-mirror-status-rollup.snapshot.json b/docs/security/security-mirror-status-rollup.snapshot.json index 3e6e1fe1..310bb4d2 100644 --- a/docs/security/security-mirror-status-rollup.snapshot.json +++ b/docs/security/security-mirror-status-rollup.snapshot.json @@ -2305,6 +2305,18 @@ "runtime_delta": false, "execution_authorized": false, "not_authorization": true + }, + { + "delta_id": "s2_153_iwooos_executive_snapshot", + "display_order": 182, + "completed_stage": "S2.153 IwoooS 高層快照層", + "progress_axis": "framework_detail", + "headline_percent_delta": 0, + "framework_delta_visible": true, + "why_headline_unchanged": "IwoooS 只新增第一屏高層快照層,把已完成可見工作、8+3 資產與主機納管、S4.9 下一卡點與 Gate 0 禁止動作壓成四張摘要卡與三條狀態軸;iwooos_executive_snapshot_card_count=4、iwooos_executive_snapshot_axis_count=3、iwooos_executive_snapshot_above_focus_deck=true、iwooos_executive_snapshot_execution_action_buttons_allowed=false、iwooos_executive_snapshot_runtime_gate_count=0、runtime_execution_authorized=false、active_runtime_gate_count=0,不把高層快照當掃描、修復、部署、主機更新、source-control mutation、GitHub primary 切換、Gitea 停用或 runtime gate。", + "runtime_delta": false, + "execution_authorized": false, + "not_authorization": true } ], "next_safe_actions": [ diff --git a/scripts/security/security-mirror-progress-guard.py b/scripts/security/security-mirror-progress-guard.py index f62eef3b..33dfe0b7 100755 --- a/scripts/security/security-mirror-progress-guard.py +++ b/scripts/security/security-mirror-progress-guard.py @@ -617,6 +617,7 @@ def validate(root: Path) -> None: "s2_150_iwooos_topology_path_explorer", "s2_151_iwooos_topology_intelligence_deck", "s2_152_iwooos_decision_runway", + "s2_153_iwooos_executive_snapshot", ] assert_equal( "progress_delta_ledger.delta_ids", @@ -1434,6 +1435,43 @@ def validate(root: Path) -> None: len(expected_iwooos_source_control_primary_readiness_item_ids), ) assert_false("iwooos_projection.summary.action_buttons_allowed", iwooos_projection["summary"]["action_buttons_allowed"]) + assert_true( + "iwooos_projection.summary.executive_snapshot_first_layer", + iwooos_projection["summary"]["executive_snapshot_first_layer"], + ) + assert_equal( + "iwooos_projection.summary.executive_snapshot_card_count", + iwooos_projection["summary"]["executive_snapshot_card_count"], + 4, + ) + assert_equal( + "iwooos_projection.summary.executive_snapshot_axis_count", + iwooos_projection["summary"]["executive_snapshot_axis_count"], + 3, + ) + assert_true( + "iwooos_projection.summary.executive_snapshot_above_focus_deck", + iwooos_projection["summary"]["executive_snapshot_above_focus_deck"], + ) + assert_true( + "iwooos_projection.summary.executive_snapshot_explains_done_next_blocked", + iwooos_projection["summary"]["executive_snapshot_explains_done_next_blocked"], + ) + assert_equal( + "iwooos_projection.summary.executive_snapshot_runtime_gate_count", + iwooos_projection["summary"]["executive_snapshot_runtime_gate_count"], + 0, + ) + assert_equal( + "iwooos_projection.summary.executive_snapshot_owner_response_received_count", + iwooos_projection["summary"]["executive_snapshot_owner_response_received_count"], + 0, + ) + assert_equal( + "iwooos_projection.summary.executive_snapshot_owner_response_accepted_count", + iwooos_projection["summary"]["executive_snapshot_owner_response_accepted_count"], + 0, + ) assert_equal( "iwooos_projection.summary.all_product_coverage_snapshot_scope_count", iwooos_projection["summary"]["all_product_coverage_snapshot_scope_count"], @@ -12011,6 +12049,154 @@ def validate(root: Path) -> None: f"iwooos_projection.first_progress_unlock_path_steps.{item['step_id']}.not_authorization", item["not_authorization"], ) + expected_executive_snapshot_card_ids = [ + "visibleWork", + "assetMesh", + "nextBlocker", + "runtimeLock", + ] + executive_snapshot_cards = iwooos_projection["executive_snapshot_cards"] + assert_equal( + "iwooos_projection.executive_snapshot_cards.ids", + [item["card_id"] for item in executive_snapshot_cards], + expected_executive_snapshot_card_ids, + ) + assert_equal( + "iwooos_projection.executive_snapshot_cards.display_order", + [item["display_order"] for item in executive_snapshot_cards], + list(range(1, len(expected_executive_snapshot_card_ids) + 1)), + ) + for item in executive_snapshot_cards: + card_id = item["card_id"] + assert_equal( + f"iwooos_projection.executive_snapshot_cards.{card_id}.display_mode", + item["display_mode"], + "first_screen_executive_snapshot", + ) + assert_false( + f"iwooos_projection.executive_snapshot_cards.{card_id}.execution_action_button_allowed", + item["execution_action_button_allowed"], + ) + assert_false( + f"iwooos_projection.executive_snapshot_cards.{card_id}.runtime_gate_opened", + item["runtime_gate_opened"], + ) + assert_false( + f"iwooos_projection.executive_snapshot_cards.{card_id}.runtime_execution_authorized", + item["runtime_execution_authorized"], + ) + assert_true( + f"iwooos_projection.executive_snapshot_cards.{card_id}.not_authorization", + item["not_authorization"], + ) + runtime_lock_card = next(item for item in executive_snapshot_cards if item["card_id"] == "runtimeLock") + for flag in ["scan_authorized", "host_change_authorized", "source_control_mutation_authorized"]: + assert_false( + f"iwooos_projection.executive_snapshot_cards.runtimeLock.{flag}", + runtime_lock_card[flag], + ) + next_blocker_card = next(item for item in executive_snapshot_cards if item["card_id"] == "nextBlocker") + for count_key in ["owner_response_received_count", "owner_response_accepted_count"]: + assert_equal( + f"iwooos_projection.executive_snapshot_cards.nextBlocker.{count_key}", + next_blocker_card[count_key], + 0, + ) + expected_executive_snapshot_axis_ids = [ + "framework", + "evidence", + "runtime", + ] + executive_snapshot_axes = iwooos_projection["executive_snapshot_axes"] + assert_equal( + "iwooos_projection.executive_snapshot_axes.ids", + [item["axis_id"] for item in executive_snapshot_axes], + expected_executive_snapshot_axis_ids, + ) + assert_equal( + "iwooos_projection.executive_snapshot_axes.display_order", + [item["display_order"] for item in executive_snapshot_axes], + list(range(1, len(expected_executive_snapshot_axis_ids) + 1)), + ) + assert_equal( + "iwooos_projection.executive_snapshot_axes.percents", + [item["percent"] for item in executive_snapshot_axes], + [88, 0, 0], + ) + for item in executive_snapshot_axes: + axis_id = item["axis_id"] + assert_equal( + f"iwooos_projection.executive_snapshot_axes.{axis_id}.display_mode", + item["display_mode"], + "first_screen_executive_snapshot_axis", + ) + assert_false( + f"iwooos_projection.executive_snapshot_axes.{axis_id}.runtime_delta", + item["runtime_delta"], + ) + assert_true( + f"iwooos_projection.executive_snapshot_axes.{axis_id}.not_authorization", + item["not_authorization"], + ) + for text in [ + 'data-testid="iwooos-executive-snapshot-board"', + 'data-testid="iwooos-executive-snapshot-overview"', + 'data-testid="iwooos-executive-snapshot-axes-grid"', + 'data-testid="iwooos-executive-snapshot-cards-grid"', + 'data-testid="iwooos-executive-snapshot-boundaries"', + "IwoooSExecutiveSnapshotBoard", + "IwoooSExecutiveSnapshotCard", + "IwoooSExecutiveSnapshotAxis", + "iwooosExecutiveSnapshotCards", + "iwooosExecutiveSnapshotAxes", + "iwooosExecutiveSnapshotBoundaries", + ]: + assert_text_contains( + "iwooos_page.executive_snapshot", + iwooos_projection_page, + text, + ) + for card_id in expected_executive_snapshot_card_ids: + assert_text_contains( + f"iwooos_page.executive_snapshot_card_{card_id}", + iwooos_projection_page, + f"data-testid={{`iwooos-executive-snapshot-card-${{item.key}}`}}", + ) + for axis_id in expected_executive_snapshot_axis_ids: + assert_text_contains( + f"iwooos_page.executive_snapshot_axis_{axis_id}", + iwooos_projection_page, + f"data-testid={{`iwooos-executive-snapshot-axis-${{item.key}}`}}", + ) + assert_text_before( + "iwooos_page.executive_snapshot_before_focus_deck", + iwooos_projection_page, + "", + "", + ) + for text in [ + "iwooos_executive_snapshot_first_layer=true", + "iwooos_executive_snapshot_card_count=4", + "iwooos_executive_snapshot_axis_count=3", + "iwooos_executive_snapshot_above_focus_deck=true", + "iwooos_executive_snapshot_explains_done_next_blocked=true", + "iwooos_executive_snapshot_execution_action_buttons_allowed=false", + "iwooos_executive_snapshot_runtime_gate_count=0", + "iwooos_executive_snapshot_owner_response_received_count=0", + "iwooos_executive_snapshot_owner_response_accepted_count=0", + "iwooos_executive_snapshot_scan_authorized=false", + "iwooos_executive_snapshot_host_change_authorized=false", + "iwooos_executive_snapshot_source_control_mutation_authorized=false", + "runtime_execution_authorized=false", + "active_runtime_gate_count=0", + "action_buttons_allowed=false", + "not_authorization=true", + ]: + assert_text_contains( + "iwooos_page.executive_snapshot_boundary", + iwooos_projection_page, + text, + ) assert_text_contains( "iwooos_page.focus_deck_testid", iwooos_projection_page, @@ -13201,6 +13387,70 @@ def validate(root: Path) -> None: list(web_messages_en["iwooos"]["commandMap"]["items"][key].keys()), field, ) + assert_contains( + "web_messages.zh-TW.iwooos.executiveSnapshot", + list(web_messages_zh["iwooos"].keys()), + "executiveSnapshot", + ) + assert_contains( + "web_messages.en.iwooos.executiveSnapshot", + list(web_messages_en["iwooos"].keys()), + "executiveSnapshot", + ) + for key in ["eyebrow", "title", "subtitle", "axes", "cards", "boundaryTitle", "boundaryIntro"]: + assert_contains( + "web_messages.zh-TW.iwooos.executiveSnapshot.keys", + list(web_messages_zh["iwooos"]["executiveSnapshot"].keys()), + key, + ) + assert_contains( + "web_messages.en.iwooos.executiveSnapshot.keys", + list(web_messages_en["iwooos"]["executiveSnapshot"].keys()), + key, + ) + for key in expected_executive_snapshot_axis_ids: + assert_contains( + "web_messages.zh-TW.iwooos.executiveSnapshot.axes", + list(web_messages_zh["iwooos"]["executiveSnapshot"]["axes"].keys()), + key, + ) + assert_contains( + "web_messages.en.iwooos.executiveSnapshot.axes", + list(web_messages_en["iwooos"]["executiveSnapshot"]["axes"].keys()), + key, + ) + assert_contains( + f"web_messages.zh-TW.iwooos.executiveSnapshot.axes.{key}", + list(web_messages_zh["iwooos"]["executiveSnapshot"]["axes"][key].keys()), + "label", + ) + assert_contains( + f"web_messages.en.iwooos.executiveSnapshot.axes.{key}", + list(web_messages_en["iwooos"]["executiveSnapshot"]["axes"][key].keys()), + "label", + ) + for key in expected_executive_snapshot_card_ids: + assert_contains( + "web_messages.zh-TW.iwooos.executiveSnapshot.cards", + list(web_messages_zh["iwooos"]["executiveSnapshot"]["cards"].keys()), + key, + ) + assert_contains( + "web_messages.en.iwooos.executiveSnapshot.cards", + list(web_messages_en["iwooos"]["executiveSnapshot"]["cards"].keys()), + key, + ) + for field in ["title", "body"]: + assert_contains( + f"web_messages.zh-TW.iwooos.executiveSnapshot.cards.{key}", + list(web_messages_zh["iwooos"]["executiveSnapshot"]["cards"][key].keys()), + field, + ) + assert_contains( + f"web_messages.en.iwooos.executiveSnapshot.cards.{key}", + list(web_messages_en["iwooos"]["executiveSnapshot"]["cards"][key].keys()), + field, + ) assert_contains( "web_messages.zh-TW.iwooos.focusDeck", list(web_messages_zh["iwooos"].keys()),