diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json
index 98fc8b98a..b8320e293 100644
--- a/apps/web/messages/en.json
+++ b/apps/web/messages/en.json
@@ -5780,10 +5780,16 @@
"label": "執行閘門"
}
},
+ "blockerFocus": {
+ "title": "目前卡在 G1:等待脫敏回覆",
+ "body": "先收五條路線的負責人角色、判定、範圍、理由與脫敏證據參照;未收到前不進預檢。",
+ "state": "阻塞焦點:S4.9 負責人回覆中繼資料",
+ "progress": "完成 0 / 3"
+ },
"nextGates": {
"redactedOwnerResponse": {
"title": "收到脫敏回覆",
- "body": "先收到五條路線的 owner response metadata。"
+ "body": "先收到五條路線的負責人回覆中繼資料。"
},
"preflightChecks": {
"title": "通過六項預檢",
diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json
index 98fc8b98a..b8320e293 100644
--- a/apps/web/messages/zh-TW.json
+++ b/apps/web/messages/zh-TW.json
@@ -5780,10 +5780,16 @@
"label": "執行閘門"
}
},
+ "blockerFocus": {
+ "title": "目前卡在 G1:等待脫敏回覆",
+ "body": "先收五條路線的負責人角色、判定、範圍、理由與脫敏證據參照;未收到前不進預檢。",
+ "state": "阻塞焦點:S4.9 負責人回覆中繼資料",
+ "progress": "完成 0 / 3"
+ },
"nextGates": {
"redactedOwnerResponse": {
"title": "收到脫敏回覆",
- "body": "先收到五條路線的 owner response metadata。"
+ "body": "先收到五條路線的負責人回覆中繼資料。"
},
"preflightChecks": {
"title": "通過六項預檢",
diff --git a/apps/web/src/app/[locale]/iwooos/page.tsx b/apps/web/src/app/[locale]/iwooos/page.tsx
index ec873c6ce..9ace9aa13 100644
--- a/apps/web/src/app/[locale]/iwooos/page.tsx
+++ b/apps/web/src/app/[locale]/iwooos/page.tsx
@@ -1060,10 +1060,22 @@ const s49OwnerResponseIntakeNextGates: S49OwnerResponseIntakeNextGate[] = [
{ key: 'humanAcceptance', gate: 'G3', value: '0 / 5', icon: ShieldCheck, tone: 'locked' },
]
+const s49OwnerResponseIntakeBlockerFocus = {
+ key: 'currentBlocker',
+ gate: 'G1',
+ value: '0 / 3',
+ icon: Clock3,
+ tone: 'warn' as const,
+}
+
const s49OwnerResponseIntakeBoundaries = [
's4_9_owner_response_intake_frontstage_lane_count=5',
's4_9_owner_response_intake_next_gate_count=3',
's4_9_owner_response_intake_next_gate_passed_count=0',
+ 's4_9_owner_response_intake_blocker_focus_count=1',
+ '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_received_count=0',
's4_9_owner_response_intake_preflight_passed_count=0',
's4_9_owner_response_intake_accepted_count=0',
@@ -10808,6 +10820,7 @@ function IwoooSS49RequestDraftDetailBoard() {
function IwoooSS49OwnerResponseIntakeBoard() {
const t = useTranslations('iwooos.s49OwnerResponseIntake')
const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const }
+ const BlockerIcon = s49OwnerResponseIntakeBlockerFocus.icon
const metrics = [
{ key: 'received', value: '0 / 5', tone: 'warn' as const },
{ key: 'preflight', value: '0 / 6', tone: 'locked' as const },
@@ -10873,6 +10886,63 @@ function IwoooSS49OwnerResponseIntakeBoard() {
))}
+
+
+
+
+
+ {t('blockerFocus.title')}
+
+
+ {t('blockerFocus.body')}
+
+
+
+
{s49OwnerResponseIntakeBlockerFocus.gate}
+
{s49OwnerResponseIntakeBlockerFocus.value}
+
+
+
+ {s49OwnerResponseIntakeNextGates.map(item => (
+
+ ))}
+
+
+ {t('blockerFocus.state')}
+ {t('blockerFocus.progress')}
+
+
None:
"s2_161_iwooos_s49_request_draft_detail_layer",
"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",
]
assert_equal(
"progress_delta_ledger.delta_ids",
@@ -1816,6 +1817,26 @@ def validate(root: Path) -> None:
iwooos_projection["summary"]["s4_9_owner_response_intake_next_gate_passed_count"],
0,
)
+ assert_equal(
+ "iwooos_projection.summary.s4_9_owner_response_intake_blocker_focus_count",
+ iwooos_projection["summary"]["s4_9_owner_response_intake_blocker_focus_count"],
+ 1,
+ )
+ assert_equal(
+ "iwooos_projection.summary.s4_9_owner_response_intake_current_blocker_gate",
+ iwooos_projection["summary"]["s4_9_owner_response_intake_current_blocker_gate"],
+ "G1",
+ )
+ assert_equal(
+ "iwooos_projection.summary.s4_9_owner_response_intake_next_gate_completion_count",
+ iwooos_projection["summary"]["s4_9_owner_response_intake_next_gate_completion_count"],
+ 0,
+ )
+ assert_equal(
+ "iwooos_projection.summary.s4_9_owner_response_intake_next_gate_total",
+ iwooos_projection["summary"]["s4_9_owner_response_intake_next_gate_total"],
+ 3,
+ )
for count_key in [
"s4_9_owner_response_intake_received_count",
"s4_9_owner_response_intake_preflight_passed_count",
@@ -13180,6 +13201,71 @@ def validate(root: Path) -> None:
f"iwooos_projection.s4_9_owner_response_intake_next_gates.{item['gate_id']}.not_authorization",
item["not_authorization"],
)
+ s4_9_owner_response_intake_blocker_focus = iwooos_projection[
+ "s4_9_owner_response_intake_blocker_focus"
+ ]
+ assert_equal(
+ "iwooos_projection.s4_9_owner_response_intake_blocker_focus.focus_id",
+ s4_9_owner_response_intake_blocker_focus["focus_id"],
+ "currentBlocker",
+ )
+ assert_equal(
+ "iwooos_projection.s4_9_owner_response_intake_blocker_focus.display_mode",
+ s4_9_owner_response_intake_blocker_focus["display_mode"],
+ "first_screen_s4_9_owner_response_intake_blocker_focus",
+ )
+ assert_equal(
+ "iwooos_projection.s4_9_owner_response_intake_blocker_focus.current_gate",
+ s4_9_owner_response_intake_blocker_focus["current_gate"],
+ "G1",
+ )
+ assert_equal(
+ "iwooos_projection.s4_9_owner_response_intake_blocker_focus.current_gate_id",
+ s4_9_owner_response_intake_blocker_focus["current_gate_id"],
+ "redactedOwnerResponse",
+ )
+ assert_equal(
+ "iwooos_projection.s4_9_owner_response_intake_blocker_focus.blocker_state",
+ s4_9_owner_response_intake_blocker_focus["blocker_state"],
+ "waiting_redacted_owner_response",
+ )
+ assert_equal(
+ "iwooos_projection.s4_9_owner_response_intake_blocker_focus.completion_count",
+ s4_9_owner_response_intake_blocker_focus["completion_count"],
+ 0,
+ )
+ assert_equal(
+ "iwooos_projection.s4_9_owner_response_intake_blocker_focus.gate_total",
+ s4_9_owner_response_intake_blocker_focus["gate_total"],
+ 3,
+ )
+ for count_key in [
+ "received_response_count",
+ "preflight_passed_count",
+ "accepted_response_count",
+ "runtime_gate_count",
+ ]:
+ assert_equal(
+ f"iwooos_projection.s4_9_owner_response_intake_blocker_focus.{count_key}",
+ s4_9_owner_response_intake_blocker_focus[count_key],
+ 0,
+ )
+ assert_false(
+ "iwooos_projection.s4_9_owner_response_intake_blocker_focus.headline_review_authorized",
+ s4_9_owner_response_intake_blocker_focus["headline_review_authorized"],
+ )
+ assert_false(
+ "iwooos_projection.s4_9_owner_response_intake_blocker_focus.runtime_execution_authorized",
+ s4_9_owner_response_intake_blocker_focus["runtime_execution_authorized"],
+ )
+ assert_false(
+ "iwooos_projection.s4_9_owner_response_intake_blocker_focus.action_buttons_allowed",
+ s4_9_owner_response_intake_blocker_focus["action_buttons_allowed"],
+ )
+ assert_true(
+ "iwooos_projection.s4_9_owner_response_intake_blocker_focus.not_authorization",
+ s4_9_owner_response_intake_blocker_focus["not_authorization"],
+ )
for text in [
'data-testid="iwooos-first-screen-depth-map-board"',
'data-testid="iwooos-first-screen-depth-map-layers"',
@@ -13423,6 +13509,8 @@ def validate(root: Path) -> None:
for text in [
'data-testid="iwooos-s49-owner-response-intake-board"',
'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-next-gates"',
"iwooos-s49-owner-response-intake-next-gate-${item.key}",
'data-testid="iwooos-s49-owner-response-intake-lanes"',
@@ -13430,8 +13518,10 @@ def validate(root: Path) -> None:
"IwoooSS49OwnerResponseIntakeBoard",
"s49OwnerResponseIntakeLanes",
"s49OwnerResponseIntakeNextGates",
+ "s49OwnerResponseIntakeBlockerFocus",
"s49OwnerResponseIntakeBoundaries",
"s49OwnerResponseIntake",
+ "blockerFocus",
"nextGatesTitle",
"nextGates",
"redactedOwnerResponse",
@@ -13477,6 +13567,10 @@ def validate(root: Path) -> None:
"s4_9_owner_response_intake_frontstage_lane_count=5",
"s4_9_owner_response_intake_next_gate_count=3",
"s4_9_owner_response_intake_next_gate_passed_count=0",
+ "s4_9_owner_response_intake_blocker_focus_count=1",
+ "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_received_count=0",
"s4_9_owner_response_intake_preflight_passed_count=0",
"s4_9_owner_response_intake_accepted_count=0",