diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index d2e12e960..0751408ef 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -1163,6 +1163,67 @@ } } }, + "surfaceConnections": { + "title": "Security Page Connection Status", + "subtitle": "Shows how the 10 existing entrypoints connect back to IwoooS: direct bridge, embedded panel bridge, or AwoooP read-only candidate. This is visible coverage only, not authorization or blocking.", + "states": { + "embeddedBridge": "Embedded bridge visible", + "directBridge": "Direct bridge visible", + "awooopCandidate": "AwoooP read-only candidate" + }, + "items": { + "securityCompliance": { + "title": "Security Compliance Hub", + "body": "Shows IwoooS inclusion through the embedded SecurityPanel and CompliancePanel bridges.", + "boundary": "Displays integration status only; no repair, approval, deploy, or blocking control is added." + }, + "legacySecurity": { + "title": "Legacy Security Monitor", + "body": "The standalone security page now shows the IwoooS read-only bridge and 58% / gate 0 boundary.", + "boundary": "Keeps error and security signals visible without turning the page into a scan entrypoint." + }, + "legacyCompliance": { + "title": "Legacy Compliance Page", + "body": "The standalone compliance page now shows the IwoooS read-only bridge and runtime false boundary.", + "boundary": "Displays compliance state only; no owner response, approval, or runtime gate is created." + }, + "alerts": { + "title": "Alert Management", + "body": "The active incident page now shows the IwoooS read-only bridge so alert signals return to the mesh.", + "boundary": "Displays alert inclusion only; no alert blocker, scan, or repair is added." + }, + "errors": { + "title": "Errors and UX Audit", + "body": "ErrorsPanel now shows the IwoooS read-only bridge so errors and UX audit stay observable.", + "boundary": "Keeps issue tracking and user friction visible without adding execution controls." + }, + "authorizations": { + "title": "Authorization Center", + "body": "The authorization page now shows the IwoooS read-only bridge while preserving HITL / multi-sig control.", + "boundary": "The bridge is not an approval record and cannot mark owner response accepted." + }, + "governance": { + "title": "AI Governance Hub", + "body": "The governance page now shows the IwoooS read-only bridge so SLOs, events, and queues remain evidence surfaces.", + "boundary": "Displays governance evidence only; visibility is not runtime authorization." + }, + "alertOperationLogs": { + "title": "Alert Operation Logs", + "body": "The operation log page now shows the dark IwoooS read-only bridge and keeps the audit chain visible.", + "boundary": "Displays event flow only; no preflight bypass, repair, or deploy is added." + }, + "awooopApprovals": { + "title": "AwoooP Approval Queue", + "body": "AwoooP approvals connect back to IwoooS through the owner-response read-only candidate.", + "boundary": "AwoooP human gate state is not security approval and cannot open runtime gates." + }, + "codeReview": { + "title": "AI Code Review Control Plane", + "body": "The Code Review page now shows the dark IwoooS read-only bridge and preserves its non-blocking review posture.", + "boundary": "Code Review is not deploy approval and does not add Gitea/GitHub actions." + } + } + }, "coverage": { "title": "Coverage and Boundary Matrix", "subtitle": "Groups the 10 existing security surfaces into four responsibility planes so IwoooS can show where to read signals, human control, governance audit, and engineering review.", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 9e33ac471..537deec0c 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -1164,6 +1164,67 @@ } } }, + "surfaceConnections": { + "title": "資安頁面連接狀態", + "subtitle": "把 10 個既有入口目前如何接回 IwoooS 說清楚:有些是直接橋接,有些是嵌入原本面板,有些是 AwoooP 只讀候選。這只是可見覆蓋,不代表授權或阻擋。", + "states": { + "embeddedBridge": "嵌入橋接可見", + "directBridge": "直接橋接可見", + "awooopCandidate": "AwoooP 只讀候選" + }, + "items": { + "securityCompliance": { + "title": "安全合規整合頁", + "body": "透過 SecurityPanel 與 CompliancePanel 的內嵌橋接顯示 IwoooS 納管狀態。", + "boundary": "只顯示整合狀態,不新增修復、批准、部署或 blocking control。" + }, + "legacySecurity": { + "title": "既有安全監控頁", + "body": "standalone 安全頁已直接顯示 IwoooS 只讀橋接與 58% / gate 0 邊界。", + "boundary": "只保留錯誤與安全訊號可見,不把頁面升級成掃描入口。" + }, + "legacyCompliance": { + "title": "既有合規頁", + "body": "standalone 合規頁已直接顯示 IwoooS 只讀橋接與 runtime false 邊界。", + "boundary": "只顯示合規狀態,不建立 owner response、approval 或 runtime gate。" + }, + "alerts": { + "title": "告警管理", + "body": "active incident 頁已直接顯示 IwoooS 只讀橋接,讓告警訊號回到資安網。", + "boundary": "只顯示告警納管狀態,不新增 alert blocker、scan 或 repair。" + }, + "errors": { + "title": "錯誤與 UX 稽核", + "body": "ErrorsPanel 已直接顯示 IwoooS 只讀橋接,讓錯誤與 UX audit 被納入觀察。", + "boundary": "只保留問題追蹤與使用者痛點可見,不新增執行控制。" + }, + "authorizations": { + "title": "授權中心", + "body": "授權頁已直接顯示 IwoooS 只讀橋接,維持 HITL / multi-sig 人控邊界。", + "boundary": "橋接不是 approval record,也不能標記 owner response accepted。" + }, + "governance": { + "title": "AI 治理中樞", + "body": "治理頁已直接顯示 IwoooS 只讀橋接,讓 SLO、events 與 queue 成為證據面。", + "boundary": "只顯示治理證據,不把治理可見性升成 runtime authorization。" + }, + "alertOperationLogs": { + "title": "告警操作日誌", + "body": "告警操作日誌已直接顯示深色 IwoooS 只讀橋接,保留稽核鏈路可見。", + "boundary": "只顯示事件流,不新增 preflight bypass、repair 或 deploy。" + }, + "awooopApprovals": { + "title": "AwoooP 審批佇列", + "body": "AwoooP approvals 以 owner response 只讀候選方式接回 IwoooS。", + "boundary": "AwoooP 人控狀態不是資安批准,也不能開 runtime gate。" + }, + "codeReview": { + "title": "AI Code Review 控制面", + "body": "Code Review 頁已直接顯示深色 IwoooS 只讀橋接,保留非阻擋審查語境。", + "boundary": "Code Review 不是部署批准,也不新增 Gitea/GitHub action。" + } + } + }, "coverage": { "title": "覆蓋與邊界矩陣", "subtitle": "把 10 個既有資安頁面分成四個責任面,讓 IwoooS 能說清楚哪裡看訊號、哪裡做人工控制、哪裡看治理稽核、哪裡看工程審查。", diff --git a/apps/web/src/app/[locale]/iwooos/page.tsx b/apps/web/src/app/[locale]/iwooos/page.tsx index e07a4fe49..ccd0d1da8 100644 --- a/apps/web/src/app/[locale]/iwooos/page.tsx +++ b/apps/web/src/app/[locale]/iwooos/page.tsx @@ -98,6 +98,14 @@ type SecuritySurface = { tone: 'steady' | 'warn' | 'locked' } +type SurfaceConnectionStatus = { + key: string + href: string + state: 'embeddedBridge' | 'directBridge' | 'awooopCandidate' + icon: typeof ShieldCheck + tone: 'steady' | 'warn' | 'locked' +} + type CoverageGroup = { key: string count: string @@ -445,6 +453,19 @@ const existingSecuritySurfaces: SecuritySurface[] = [ { key: 'codeReview', href: '/code-review', icon: SearchCheck, tone: 'warn' }, ] +const surfaceConnectionStatuses: SurfaceConnectionStatus[] = [ + { key: 'securityCompliance', href: '/security-compliance', state: 'embeddedBridge', icon: ShieldCheck, tone: 'steady' }, + { key: 'legacySecurity', href: '/security', state: 'directBridge', icon: Radar, tone: 'warn' }, + { key: 'legacyCompliance', href: '/compliance', state: 'directBridge', icon: ClipboardCheck, tone: 'warn' }, + { key: 'alerts', href: '/alerts', state: 'directBridge', icon: Bell, tone: 'warn' }, + { key: 'errors', href: '/errors', state: 'directBridge', icon: FileWarning, tone: 'warn' }, + { key: 'authorizations', href: '/authorizations', state: 'directBridge', icon: Lock, tone: 'locked' }, + { key: 'governance', href: '/governance', state: 'directBridge', icon: ShieldCheck, tone: 'locked' }, + { key: 'alertOperationLogs', href: '/alert-operation-logs', state: 'directBridge', icon: ListChecks, tone: 'steady' }, + { key: 'awooopApprovals', href: '/awooop/approvals', state: 'awooopCandidate', icon: ClipboardCheck, tone: 'locked' }, + { key: 'codeReview', href: '/code-review', state: 'directBridge', icon: SearchCheck, tone: 'warn' }, +] + const coverageGroups: CoverageGroup[] = [ { key: 'signals', @@ -1216,6 +1237,60 @@ function SurfaceCard({ item, locale }: { item: SecuritySurface; locale: string } ) } +function SurfaceConnectionCard({ item, locale }: { item: SurfaceConnectionStatus; locale: string }) { + const t = useTranslations('iwooos.surfaceConnections') + const Icon = item.icon + return ( + +
+
+ + + {t(`items.${item.key}.title` as never)} + +
+ + {item.href} + +
+
+ {t(`states.${item.state}` as never)} +
+

+ {t(`items.${item.key}.body` as never)} +

+
+ + {t(`items.${item.key}.boundary` as never)} +
+ + ) +} + function CoverageCard({ item }: { item: CoverageGroup }) { const t = useTranslations('iwooos.coverage') const Icon = item.icon @@ -3212,6 +3287,26 @@ export default function IwoooSPage({ params }: { params: { locale: string } }) { +
+
+

{t('surfaceConnections.title')}

+

+ {t('surfaceConnections.subtitle')} +

+
+
+ {surfaceConnectionStatuses.map(item => ( + + ))} +
+
+
None: primary_gate = load_json(security_dir / "source-control-primary-readiness-gate.snapshot.json") rollout_policy = load_json(security_dir / "security-rollout-policy.snapshot.json") iwooos_projection = load_json(security_dir / "iwooos-posture-projection.snapshot.json") + iwooos_projection_page = ( + root / "apps" / "web" / "src" / "app" / "[locale]" / "iwooos" / "page.tsx" + ).read_text(encoding="utf-8") awooop_home_page = ( root / "apps" / "web" / "src" / "app" / "[locale]" / "awooop" / "page.tsx" ).read_text(encoding="utf-8") @@ -271,6 +274,7 @@ def validate(root: Path) -> None: "s2_59_existing_security_pages_iwooos_reverse_bridge", "s2_60_security_control_pages_iwooos_reverse_bridge", "s2_61_audit_engineering_pages_iwooos_reverse_bridge", + "s2_62_iwooos_frontend_surface_connection_board", ] assert_equal( "progress_delta_ledger.delta_ids", @@ -345,6 +349,11 @@ def validate(root: Path) -> None: [item["action_id"] for item in rollup["next_safe_actions"] if isinstance(item, dict)], "show_audit_engineering_pages_iwooos_reverse_bridge", ) + assert_contains( + "rollup.next_safe_actions.action_ids", + [item["action_id"] for item in rollup["next_safe_actions"] if isinstance(item, dict)], + "show_iwooos_frontend_surface_connection_board", + ) assert_equal("rollout_policy.schema_version", rollout_policy["schema_version"], "security_rollout_policy_v1") assert_equal("rollout_policy.default_mode", rollout_policy["default_mode"], "observe") @@ -454,6 +463,11 @@ def validate(root: Path) -> None: iwooos_projection["summary"]["existing_frontend_surface_count"], len(expected_iwooos_surface_ids), ) + assert_equal( + "iwooos_projection.summary.frontend_surface_reverse_bridge_status_count", + iwooos_projection["summary"]["frontend_surface_reverse_bridge_status_count"], + len(expected_iwooos_surface_ids), + ) expected_iwooos_coverage_group_ids = [ "signals_and_exposure", "human_control_boundary", @@ -1709,6 +1723,52 @@ def validate(root: Path) -> None: f"iwooos_projection.existing_frontend_surfaces.{item['surface_id']}.not_authorization", item["not_authorization"], ) + iwooos_surface_bridge_statuses = iwooos_projection["frontend_surface_reverse_bridge_statuses"] + assert_equal( + "iwooos_projection.frontend_surface_reverse_bridge_statuses.ids", + [item["surface_id"] for item in iwooos_surface_bridge_statuses], + expected_iwooos_surface_ids, + ) + assert_equal( + "iwooos_projection.frontend_surface_reverse_bridge_statuses.display_order", + [item["display_order"] for item in iwooos_surface_bridge_statuses], + list(range(1, len(expected_iwooos_surface_ids) + 1)), + ) + expected_iwooos_surface_bridge_states = [ + "embedded_bridge_visible", + "direct_bridge_visible", + "direct_bridge_visible", + "direct_bridge_visible", + "direct_bridge_visible", + "direct_bridge_visible", + "direct_bridge_visible", + "direct_bridge_visible", + "awooop_candidate_visible", + "direct_bridge_visible", + ] + assert_equal( + "iwooos_projection.frontend_surface_reverse_bridge_statuses.states", + [item["reverse_bridge_state"] for item in iwooos_surface_bridge_statuses], + expected_iwooos_surface_bridge_states, + ) + for item in iwooos_surface_bridge_statuses: + assert_equal( + f"iwooos_projection.frontend_surface_reverse_bridge_statuses.{item['surface_id']}.display_mode", + item["display_mode"], + "reverse_bridge_status_only", + ) + assert_false( + f"iwooos_projection.frontend_surface_reverse_bridge_statuses.{item['surface_id']}.runtime_execution_authorized", + item["runtime_execution_authorized"], + ) + assert_false( + f"iwooos_projection.frontend_surface_reverse_bridge_statuses.{item['surface_id']}.action_buttons_allowed", + item["action_buttons_allowed"], + ) + assert_true( + f"iwooos_projection.frontend_surface_reverse_bridge_statuses.{item['surface_id']}.not_authorization", + item["not_authorization"], + ) iwooos_coverage_groups = iwooos_projection["frontend_surface_coverage_groups"] assert_equal( "iwooos_projection.frontend_surface_coverage_groups.ids", @@ -4621,6 +4681,7 @@ def validate(root: Path) -> None: "display_s4_9_owner_response_request_templates", "display_non_blocking_lanes", "display_existing_frontend_security_surfaces", + "display_frontend_surface_reverse_bridge_statuses", "display_frontend_surface_coverage_matrix", "display_frontend_surface_conflict_controls", "display_operator_journey_steps", @@ -4680,6 +4741,10 @@ def validate(root: Path) -> None: "mark_host_evidence_received", "mark_host_evidence_accepted", "ingest_raw_host_evidence", + "treat_reverse_bridge_status_as_authorization", + "create_runtime_gate_from_reverse_bridge_status", + "create_code_review_blocker_from_reverse_bridge_status", + "create_gitea_or_github_action_from_reverse_bridge_status", "advance_host_collection_state", "skip_host_evidence_dependency", "accept_host_evidence_without_preflight", @@ -5129,6 +5194,11 @@ def validate(root: Path) -> None: source_text, '', ) + assert_text_contains("iwooos_page.surface_connection_board", iwooos_projection_page, "surfaceConnectionStatuses") + assert_text_contains("iwooos_page.surface_connection_testid", iwooos_projection_page, 'data-testid="iwooos-surface-connection-board"') + assert_text_contains("iwooos_page.surface_connection_embedded", iwooos_projection_page, "embeddedBridge") + assert_text_contains("iwooos_page.surface_connection_direct", iwooos_projection_page, "directBridge") + assert_text_contains("iwooos_page.surface_connection_awooop", iwooos_projection_page, "awooopCandidate") for key in [ "title", "subtitle", @@ -5161,6 +5231,50 @@ def validate(root: Path) -> None: list(web_messages_en["security"]["iwooosBridge"]["metrics"].keys()), key, ) + for key in ["title", "subtitle", "states", "items"]: + assert_contains( + "web_messages.zh-TW.iwooos.surfaceConnections", + list(web_messages_zh["iwooos"]["surfaceConnections"].keys()), + key, + ) + assert_contains( + "web_messages.en.iwooos.surfaceConnections", + list(web_messages_en["iwooos"]["surfaceConnections"].keys()), + key, + ) + for key in ["embeddedBridge", "directBridge", "awooopCandidate"]: + assert_contains( + "web_messages.zh-TW.iwooos.surfaceConnections.states", + list(web_messages_zh["iwooos"]["surfaceConnections"]["states"].keys()), + key, + ) + assert_contains( + "web_messages.en.iwooos.surfaceConnections.states", + list(web_messages_en["iwooos"]["surfaceConnections"]["states"].keys()), + key, + ) + for key in [ + "securityCompliance", + "legacySecurity", + "legacyCompliance", + "alerts", + "errors", + "authorizations", + "governance", + "alertOperationLogs", + "awooopApprovals", + "codeReview", + ]: + assert_contains( + "web_messages.zh-TW.iwooos.surfaceConnections.items", + list(web_messages_zh["iwooos"]["surfaceConnections"]["items"].keys()), + key, + ) + assert_contains( + "web_messages.en.iwooos.surfaceConnections.items", + list(web_messages_en["iwooos"]["surfaceConnections"]["items"].keys()), + key, + ) owner_summary = owner_rollup["summary"] assert_equal("owner_rollup.total_received_response_count", owner_summary["total_received_response_count"], 0)