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",