From abe4f5fead9eb576b517b962d3b3778f0670490d Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 2 Jun 2026 11:49:45 +0800 Subject: [PATCH] fix(web): add IwoooS S4.9 delivery cards --- apps/web/messages/en.json | 28 +++-- apps/web/messages/zh-TW.json | 28 +++-- apps/web/src/app/[locale]/iwooos/page.tsx | 53 +++++++++ .../iwooos-posture-projection.snapshot.json | 47 ++++++++ ...ecurity-mirror-status-rollup.snapshot.json | 14 ++- .../security-mirror-progress-guard.py | 105 ++++++++++++++++++ 6 files changed, 260 insertions(+), 15 deletions(-) diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 0ef1bdd6d..dd4f13290 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -5765,7 +5765,7 @@ "s49OwnerResponseIntake": { "eyebrow": "S4.9 收件預檢指揮板", "title": "五條回覆路線目前都還在等待", - "subtitle": "把 S4.9 負責人回覆收件、預檢、驗收與執行閘門濃縮成首屏狀態列;目前只顯示 read-only 進度,不送件、不收件、不接受、不開 runtime。", + "subtitle": "把 S4.9 負責人回覆收件、預檢、驗收與執行閘門濃縮成首屏狀態列;目前只顯示只讀進度,不送件、不收件、不接受、不開執行期。", "laneStatus": "狀態", "nextGatesTitle": "下一步門檻", "boundaryTitle": "收件預檢邊界", @@ -5794,6 +5794,20 @@ "state": "阻塞焦點:S4.9 負責人回覆中繼資料", "progress": "完成 0 / 3" }, + "deliveryItems": { + "ownerDecision": { + "title": "負責人判定", + "body": "角色、範圍、理由與後續負責人要可追溯。" + }, + "redactedEvidence": { + "title": "脫敏證據參照", + "body": "只收證據指標,不收明文機密或原始載荷。" + }, + "reviewTrail": { + "title": "預檢驗收紀錄", + "body": "六項預檢與人工接受目前都還是 0。" + } + }, "nextGates": { "redactedOwnerResponse": { "title": "收到脫敏回覆", @@ -5805,29 +5819,29 @@ }, "humanAcceptance": { "title": "人工驗收接受", - "body": "五條都接受後才可進 headline review。" + "body": "五條都接受後才可進整體進度審查。" } }, "items": { "publicGap": { "title": "D1 公開庫差異回覆", - "body": "等待 owner 判定 public-only 與 local Gitea 差異是否納入目前階段範圍。" + "body": "等待負責人判定公開庫與本地 Gitea 差異是否納入目前階段範圍。" }, "namespaceIdentity": { "title": "D2 命名空間身分回覆", - "body": "等待 owner 判定 Gitea `wooo` 應以 user、org 或雙軌方式盤點。" + "body": "等待負責人判定 Gitea `wooo` 應以使用者、組織或雙軌方式盤點。" }, "adjacentScope": { "title": "D3 110 鄰近來源回覆", - "body": "等待 owner 逐項判定 bitan-pharmacy、momo-pro-system、tsenyang-website 與 infra 設定來源。" + "body": "等待負責人逐項判定 bitan-pharmacy、momo-pro-system、tsenyang-website 與基礎設施設定來源。" }, "canonicalOwner": { "title": "D4 正本來源與負責人回覆", - "body": "等待 owner 指定 canonical source、GitHub target candidate 與 visibility review owner。" + "body": "等待負責人指定正本來源、GitHub 目標候選與可見性審查負責人。" }, "legacyDisposition": { "title": "D5 Legacy / 不可存取處置回覆", - "body": "等待 owner 對 legacy、inaccessible 或 external repo 留下 disposition 與後續負責人。" + "body": "等待負責人對舊版、不可存取或外部專案庫留下處置判定與後續負責人。" } } }, diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 0ef1bdd6d..dd4f13290 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -5765,7 +5765,7 @@ "s49OwnerResponseIntake": { "eyebrow": "S4.9 收件預檢指揮板", "title": "五條回覆路線目前都還在等待", - "subtitle": "把 S4.9 負責人回覆收件、預檢、驗收與執行閘門濃縮成首屏狀態列;目前只顯示 read-only 進度,不送件、不收件、不接受、不開 runtime。", + "subtitle": "把 S4.9 負責人回覆收件、預檢、驗收與執行閘門濃縮成首屏狀態列;目前只顯示只讀進度,不送件、不收件、不接受、不開執行期。", "laneStatus": "狀態", "nextGatesTitle": "下一步門檻", "boundaryTitle": "收件預檢邊界", @@ -5794,6 +5794,20 @@ "state": "阻塞焦點:S4.9 負責人回覆中繼資料", "progress": "完成 0 / 3" }, + "deliveryItems": { + "ownerDecision": { + "title": "負責人判定", + "body": "角色、範圍、理由與後續負責人要可追溯。" + }, + "redactedEvidence": { + "title": "脫敏證據參照", + "body": "只收證據指標,不收明文機密或原始載荷。" + }, + "reviewTrail": { + "title": "預檢驗收紀錄", + "body": "六項預檢與人工接受目前都還是 0。" + } + }, "nextGates": { "redactedOwnerResponse": { "title": "收到脫敏回覆", @@ -5805,29 +5819,29 @@ }, "humanAcceptance": { "title": "人工驗收接受", - "body": "五條都接受後才可進 headline review。" + "body": "五條都接受後才可進整體進度審查。" } }, "items": { "publicGap": { "title": "D1 公開庫差異回覆", - "body": "等待 owner 判定 public-only 與 local Gitea 差異是否納入目前階段範圍。" + "body": "等待負責人判定公開庫與本地 Gitea 差異是否納入目前階段範圍。" }, "namespaceIdentity": { "title": "D2 命名空間身分回覆", - "body": "等待 owner 判定 Gitea `wooo` 應以 user、org 或雙軌方式盤點。" + "body": "等待負責人判定 Gitea `wooo` 應以使用者、組織或雙軌方式盤點。" }, "adjacentScope": { "title": "D3 110 鄰近來源回覆", - "body": "等待 owner 逐項判定 bitan-pharmacy、momo-pro-system、tsenyang-website 與 infra 設定來源。" + "body": "等待負責人逐項判定 bitan-pharmacy、momo-pro-system、tsenyang-website 與基礎設施設定來源。" }, "canonicalOwner": { "title": "D4 正本來源與負責人回覆", - "body": "等待 owner 指定 canonical source、GitHub target candidate 與 visibility review owner。" + "body": "等待負責人指定正本來源、GitHub 目標候選與可見性審查負責人。" }, "legacyDisposition": { "title": "D5 Legacy / 不可存取處置回覆", - "body": "等待 owner 對 legacy、inaccessible 或 external repo 留下 disposition 與後續負責人。" + "body": "等待負責人對舊版、不可存取或外部專案庫留下處置判定與後續負責人。" } } }, diff --git a/apps/web/src/app/[locale]/iwooos/page.tsx b/apps/web/src/app/[locale]/iwooos/page.tsx index 9ace9aa13..362e9999a 100644 --- a/apps/web/src/app/[locale]/iwooos/page.tsx +++ b/apps/web/src/app/[locale]/iwooos/page.tsx @@ -98,6 +98,13 @@ type S49OwnerResponseIntakeNextGate = { tone: 'steady' | 'warn' | 'locked' } +type S49OwnerResponseIntakeDeliveryItem = { + key: string + value: string + icon: typeof ShieldCheck + tone: 'steady' | 'warn' | 'locked' +} + type FastProgressItem = { key: string value: string @@ -1068,6 +1075,12 @@ const s49OwnerResponseIntakeBlockerFocus = { tone: 'warn' as const, } +const s49OwnerResponseIntakeDeliveryItems: S49OwnerResponseIntakeDeliveryItem[] = [ + { key: 'ownerDecision', value: '待補', icon: ClipboardCheck, tone: 'warn' }, + { key: 'redactedEvidence', value: '0', icon: FileText, tone: 'locked' }, + { key: 'reviewTrail', value: '0', icon: SearchCheck, tone: 'locked' }, +] + const s49OwnerResponseIntakeBoundaries = [ 's4_9_owner_response_intake_frontstage_lane_count=5', 's4_9_owner_response_intake_next_gate_count=3', @@ -1076,6 +1089,9 @@ const s49OwnerResponseIntakeBoundaries = [ 's4_9_owner_response_intake_current_blocker_gate=G1', 's4_9_owner_response_intake_next_gate_completion_count=0', 's4_9_owner_response_intake_next_gate_total=3', + 's4_9_owner_response_intake_delivery_card_count=3', + 's4_9_owner_response_intake_delivery_card_completed_count=0', + 's4_9_owner_response_intake_delivery_raw_payload_allowed=false', 's4_9_owner_response_intake_received_count=0', 's4_9_owner_response_intake_preflight_passed_count=0', 's4_9_owner_response_intake_accepted_count=0', @@ -10943,6 +10959,43 @@ function IwoooSS49OwnerResponseIntakeBoard() { {t('blockerFocus.progress')} +
+ {s49OwnerResponseIntakeDeliveryItems.map(item => { + const Icon = item.icon + return ( +
+
+ + {item.value} +
+
+ {t(`deliveryItems.${item.key}.title` as never)} +
+
+ {t(`deliveryItems.${item.key}.body` as never)} +
+
+ ) + })} +
None: "s2_162_iwooos_s49_owner_response_intake_first_layer", "s2_163_iwooos_s49_owner_response_intake_next_gates", "s2_164_iwooos_s49_owner_response_intake_blocker_focus", + "s2_165_iwooos_s49_owner_response_delivery_cards", ] assert_equal( "progress_delta_ledger.delta_ids", @@ -1837,6 +1838,20 @@ def validate(root: Path) -> None: iwooos_projection["summary"]["s4_9_owner_response_intake_next_gate_total"], 3, ) + assert_equal( + "iwooos_projection.summary.s4_9_owner_response_intake_delivery_card_count", + iwooos_projection["summary"]["s4_9_owner_response_intake_delivery_card_count"], + 3, + ) + assert_equal( + "iwooos_projection.summary.s4_9_owner_response_intake_delivery_card_completed_count", + iwooos_projection["summary"]["s4_9_owner_response_intake_delivery_card_completed_count"], + 0, + ) + assert_false( + "iwooos_projection.summary.s4_9_owner_response_intake_delivery_raw_payload_allowed", + iwooos_projection["summary"]["s4_9_owner_response_intake_delivery_raw_payload_allowed"], + ) for count_key in [ "s4_9_owner_response_intake_received_count", "s4_9_owner_response_intake_preflight_passed_count", @@ -13266,6 +13281,68 @@ def validate(root: Path) -> None: "iwooos_projection.s4_9_owner_response_intake_blocker_focus.not_authorization", s4_9_owner_response_intake_blocker_focus["not_authorization"], ) + s4_9_owner_response_intake_delivery_cards = iwooos_projection[ + "s4_9_owner_response_intake_delivery_cards" + ] + assert_equal( + "iwooos_projection.s4_9_owner_response_intake_delivery_cards.ids", + [item["card_id"] for item in s4_9_owner_response_intake_delivery_cards], + ["ownerDecision", "redactedEvidence", "reviewTrail"], + ) + assert_equal( + "iwooos_projection.s4_9_owner_response_intake_delivery_cards.display_order", + [item["display_order"] for item in s4_9_owner_response_intake_delivery_cards], + [1, 2, 3], + ) + assert_equal( + "iwooos_projection.s4_9_owner_response_intake_delivery_cards.required_evidence", + [item["required_evidence"] for item in s4_9_owner_response_intake_delivery_cards], + ["負責人角色、判定、範圍、理由與後續負責人", "脫敏證據參照", "預檢驗收紀錄"], + ) + assert_equal( + "iwooos_projection.s4_9_owner_response_intake_delivery_cards.current_states", + [item["current_state"] for item in s4_9_owner_response_intake_delivery_cards], + [ + "waiting_owner_response", + "waiting_redacted_evidence_pointer", + "waiting_preflight_and_acceptance", + ], + ) + for item in s4_9_owner_response_intake_delivery_cards: + assert_equal( + f"iwooos_projection.s4_9_owner_response_intake_delivery_cards.{item['card_id']}.display_mode", + item["display_mode"], + "first_screen_s4_9_owner_response_intake_delivery_card", + ) + assert_equal( + f"iwooos_projection.s4_9_owner_response_intake_delivery_cards.{item['card_id']}.completed_count", + item["completed_count"], + 0, + ) + assert_false( + f"iwooos_projection.s4_9_owner_response_intake_delivery_cards.{item['card_id']}.raw_payload_allowed", + item["raw_payload_allowed"], + ) + assert_false( + f"iwooos_projection.s4_9_owner_response_intake_delivery_cards.{item['card_id']}.secret_plaintext_allowed", + item["secret_plaintext_allowed"], + ) + assert_false( + f"iwooos_projection.s4_9_owner_response_intake_delivery_cards.{item['card_id']}.headline_review_authorized", + item["headline_review_authorized"], + ) + assert_false( + f"iwooos_projection.s4_9_owner_response_intake_delivery_cards.{item['card_id']}.runtime_execution_authorized", + item["runtime_execution_authorized"], + ) + assert_false( + f"iwooos_projection.s4_9_owner_response_intake_delivery_cards.{item['card_id']}.action_buttons_allowed", + item["action_buttons_allowed"], + ) + assert_true( + f"iwooos_projection.s4_9_owner_response_intake_delivery_cards.{item['card_id']}.not_authorization", + item["not_authorization"], + ) for text in [ 'data-testid="iwooos-first-screen-depth-map-board"', 'data-testid="iwooos-first-screen-depth-map-layers"', @@ -13511,6 +13588,8 @@ def validate(root: Path) -> None: 'data-testid="iwooos-s49-owner-response-intake-metrics"', 'data-testid="iwooos-s49-owner-response-intake-blocker-focus"', 'data-testid="iwooos-s49-owner-response-intake-completion-rail"', + 'data-testid="iwooos-s49-owner-response-intake-delivery-cards"', + "iwooos-s49-owner-response-intake-delivery-card-${item.key}", 'data-testid="iwooos-s49-owner-response-intake-next-gates"', "iwooos-s49-owner-response-intake-next-gate-${item.key}", 'data-testid="iwooos-s49-owner-response-intake-lanes"', @@ -13519,9 +13598,14 @@ def validate(root: Path) -> None: "s49OwnerResponseIntakeLanes", "s49OwnerResponseIntakeNextGates", "s49OwnerResponseIntakeBlockerFocus", + "s49OwnerResponseIntakeDeliveryItems", "s49OwnerResponseIntakeBoundaries", "s49OwnerResponseIntake", "blockerFocus", + "deliveryItems", + "ownerDecision", + "redactedEvidence", + "reviewTrail", "nextGatesTitle", "nextGates", "redactedOwnerResponse", @@ -13551,6 +13635,24 @@ def validate(root: Path) -> None: iwooos_projection_page, text, ) + s49_owner_response_intake_strings = "\n".join( + collect_string_values(web_messages_zh["iwooos"]["s49OwnerResponseIntake"]) + ) + for forbidden in [ + "read-only", + "runtime", + "headline review", + "owner role", + "owner response metadata", + "evidence refs", + "canonical source", + "visibility review owner", + ]: + assert_text_not_contains( + "web_messages.zh-TW.iwooos.s49OwnerResponseIntake.forbidden_english_terms", + s49_owner_response_intake_strings, + forbidden, + ) assert_text_before( "iwooos_page.s49_request_draft_detail_before_owner_response_intake", iwooos_projection_page, @@ -13571,6 +13673,9 @@ def validate(root: Path) -> None: "s4_9_owner_response_intake_current_blocker_gate=G1", "s4_9_owner_response_intake_next_gate_completion_count=0", "s4_9_owner_response_intake_next_gate_total=3", + "s4_9_owner_response_intake_delivery_card_count=3", + "s4_9_owner_response_intake_delivery_card_completed_count=0", + "s4_9_owner_response_intake_delivery_raw_payload_allowed=false", "s4_9_owner_response_intake_received_count=0", "s4_9_owner_response_intake_preflight_passed_count=0", "s4_9_owner_response_intake_accepted_count=0",