From f0daaccbbad58c312e4e2c93b561a85cecff663f Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 1 Jun 2026 20:48:12 +0800 Subject: [PATCH] fix(web): add IwoooS S4.9 draft detail layer --- apps/web/messages/en.json | 49 ++++ apps/web/messages/zh-TW.json | 49 ++++ apps/web/src/app/[locale]/iwooos/page.tsx | 217 ++++++++++++++++++ docs/LOGBOOK.md | 49 ++++ ...er-attestation-request-draft.snapshot.json | 4 + .../iwooos-posture-projection.snapshot.json | 105 +++++++++ ...ecurity-mirror-status-rollup.snapshot.json | 12 + .../security-mirror-progress-guard.py | 185 +++++++++++++++ 8 files changed, 670 insertions(+) diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 2ea8161c..b7ad64cb 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -5694,6 +5694,55 @@ } } }, + "s49RequestDraftDetail": { + "eyebrow": "S4.9 補件草稿詳情層", + "title": "每一題都能看見欄位、禁區與證據格式", + "subtitle": "將五份草稿拆成填寫檢查表,讓負責人知道需要回覆哪些欄位;目前仍只收脫敏 evidence refs,不收機密、不送出請求、不開閘門。", + "draftStatus": "待負責人確認", + "boundaryTitle": "詳情層邊界", + "labels": { + "required": "必要欄位", + "forbidden": "禁止事項", + "evidence": "證據格式", + "redactedRefs": "脫敏 refs" + }, + "metrics": { + "rows": { + "label": "草稿題目", + "value": "5 題" + }, + "fields": { + "label": "必要欄位", + "value": "30 欄" + }, + "gate": { + "label": "執行閘門", + "value": "0" + } + }, + "items": { + "publicGap": { + "title": "D1 公開庫差異判定", + "body": "需填 owner role/team、decision、reason、affected repos、evidence refs、followup owner。" + }, + "namespaceIdentity": { + "title": "D2 命名空間身分判定", + "body": "需填 owner role/team、decision、reason、canonical namespace、evidence refs、followup owner。" + }, + "adjacentScope": { + "title": "D3 鄰近來源範圍判定", + "body": "需填 owner role/team、decision、reason、affected sources、evidence refs、followup owner。" + }, + "canonicalOwner": { + "title": "D4 正本來源與負責人判定", + "body": "需填 owner role/team、decision、reason、affected repos、evidence refs、followup owner。" + }, + "legacyDisposition": { + "title": "D5 Legacy / 不可存取處置判定", + "body": "需填 owner role/team、decision、reason、affected repos、evidence refs、followup owner。" + } + } + }, "immediateVisualMesh": { "eyebrow": "首屏資安網視覺模型", "title": "資安關聯視覺模型", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 2ea8161c..b7ad64cb 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -5694,6 +5694,55 @@ } } }, + "s49RequestDraftDetail": { + "eyebrow": "S4.9 補件草稿詳情層", + "title": "每一題都能看見欄位、禁區與證據格式", + "subtitle": "將五份草稿拆成填寫檢查表,讓負責人知道需要回覆哪些欄位;目前仍只收脫敏 evidence refs,不收機密、不送出請求、不開閘門。", + "draftStatus": "待負責人確認", + "boundaryTitle": "詳情層邊界", + "labels": { + "required": "必要欄位", + "forbidden": "禁止事項", + "evidence": "證據格式", + "redactedRefs": "脫敏 refs" + }, + "metrics": { + "rows": { + "label": "草稿題目", + "value": "5 題" + }, + "fields": { + "label": "必要欄位", + "value": "30 欄" + }, + "gate": { + "label": "執行閘門", + "value": "0" + } + }, + "items": { + "publicGap": { + "title": "D1 公開庫差異判定", + "body": "需填 owner role/team、decision、reason、affected repos、evidence refs、followup owner。" + }, + "namespaceIdentity": { + "title": "D2 命名空間身分判定", + "body": "需填 owner role/team、decision、reason、canonical namespace、evidence refs、followup owner。" + }, + "adjacentScope": { + "title": "D3 鄰近來源範圍判定", + "body": "需填 owner role/team、decision、reason、affected sources、evidence refs、followup owner。" + }, + "canonicalOwner": { + "title": "D4 正本來源與負責人判定", + "body": "需填 owner role/team、decision、reason、affected repos、evidence refs、followup owner。" + }, + "legacyDisposition": { + "title": "D5 Legacy / 不可存取處置判定", + "body": "需填 owner role/team、decision、reason、affected repos、evidence refs、followup owner。" + } + } + }, "immediateVisualMesh": { "eyebrow": "首屏資安網視覺模型", "title": "資安關聯視覺模型", diff --git a/apps/web/src/app/[locale]/iwooos/page.tsx b/apps/web/src/app/[locale]/iwooos/page.tsx index dfdb76f8..d0305428 100644 --- a/apps/web/src/app/[locale]/iwooos/page.tsx +++ b/apps/web/src/app/[locale]/iwooos/page.tsx @@ -71,6 +71,15 @@ type S49RequestDraftPackageItem = { tone: 'steady' | 'warn' | 'locked' } +type S49RequestDraftDetailItem = { + key: string + template: string + requiredCount: string + forbiddenCount: string + icon: typeof ShieldCheck + tone: 'steady' | 'warn' | 'locked' +} + type FastProgressItem = { key: string value: string @@ -995,6 +1004,30 @@ const s49RequestDraftPackageBoundaries = [ 'not_authorization=true', ] +const s49RequestDraftDetailItems: S49RequestDraftDetailItem[] = [ + { key: 'publicGap', template: 'D1', requiredCount: '6 欄', forbiddenCount: '10 禁', icon: Radar, tone: 'warn' }, + { key: 'namespaceIdentity', template: 'D2', requiredCount: '6 欄', forbiddenCount: '10 禁', icon: Network, tone: 'warn' }, + { key: 'adjacentScope', template: 'D3', requiredCount: '6 欄', forbiddenCount: '10 禁', icon: Server, tone: 'warn' }, + { key: 'canonicalOwner', template: 'D4', requiredCount: '6 欄', forbiddenCount: '10 禁', icon: GitBranch, tone: 'warn' }, + { key: 'legacyDisposition', template: 'D5', requiredCount: '6 欄', forbiddenCount: '10 禁', icon: FileWarning, tone: 'warn' }, +] + +const s49RequestDraftDetailBoundaries = [ + 's4_9_owner_attestation_request_draft_detail_frontstage_row_count=5', + 's4_9_owner_attestation_request_draft_detail_required_field_total=30', + 's4_9_owner_attestation_request_draft_detail_forbidden_action_count=10', + 's4_9_owner_attestation_request_sent=false', + 's4_9_owner_attestation_owner_response_received_count=0', + 's4_9_owner_attestation_owner_response_accepted_count=0', + 's4_9_owner_attestation_runtime_gate_opened=false', + 'redacted_evidence_refs_only=true', + 'secret_plaintext_collection_allowed=false', + 'runtime_execution_authorized=false', + 'active_runtime_gate_count=0', + 'action_buttons_allowed=false', + 'not_authorization=true', +] + const fastProgressItems: FastProgressItem[] = [ { key: 'done', value: '7 頁', icon: CheckCircle2, tone: 'steady' }, { key: 'kali112', value: '只讀通過', icon: ShieldCheck, tone: 'warn' }, @@ -4351,6 +4384,28 @@ const band = { borderRadius: 8, } +const miniMetricStyle = { + border: '0.5px solid #e4dfc8', + borderRadius: 8, + padding: 7, + display: 'grid', + gap: 3, + minWidth: 0, +} + +const miniMetricLabelStyle = { + color: '#787b64', + fontSize: 10, + fontWeight: 700, +} + +const miniMetricValueStyle = { + color: '#4f6f21', + fontSize: 13, + fontWeight: 900, + lineHeight: 1.15, +} + function IwoooSSectionGroup({ id, title, @@ -10540,6 +10595,167 @@ function IwoooSS49RequestDraftPackageBoard() { ) } +function IwoooSS49RequestDraftDetailBoard() { + const t = useTranslations('iwooos.s49RequestDraftDetail') + const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const } + + return ( +
+
+
+
+
+ + {t('eyebrow')} +
+

{t('title')}

+

{t('subtitle')}

+
+ {['rows', 'fields', 'gate'].map(metric => ( +
+
{t(`metrics.${metric}.label` as never)}
+
+ {t(`metrics.${metric}.value` as never)} +
+
+ ))} +
+
+ +
+ {s49RequestDraftDetailItems.map(item => { + const Icon = item.icon + return ( +
+
+
+ + {item.template} + {t('draftStatus')} +
+

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

+

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

+
+ +
+
+ {t('labels.required')} + {item.requiredCount} +
+
+ {t('labels.forbidden')} + {item.forbiddenCount} +
+
+ {t('labels.evidence')} + {t('labels.redactedRefs')} +
+
+
+ ) + })} +
+
+ +
+ + {t('boundaryTitle')} + +
+ {s49RequestDraftDetailBoundaries.map(item => ( + + {item} + + ))} +
+
+
+
+ ) +} + function IwoooSImmediateVisualMeshBoard() { const t = useTranslations('iwooos.immediateVisualMesh') const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const } @@ -17020,6 +17236,7 @@ export default function IwoooSPage({ params }: { params: { locale: string } }) { + diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index d4ef5da6..85988131 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,52 @@ +## 2026-06-01|IwoooS S4.9 補件草稿詳情層落地 + +**背景**: + +- S4.9 前台已能看到五張補件題目卡,但使用者仍需要知道每一題要填哪些欄位、不能做哪些事,以及為何尚未開啟執行閘門。 +- 本階段延續初期資安框架原則:只做可視化、read-only evidence 與人工 gate;不啟動 Kali 掃描、SSH 修復、主機更新、repo 變更或 runtime execution。 + +**本次調整**: + +- `apps/web/src/app/[locale]/iwooos/page.tsx`: + - 新增 `IwoooSS49RequestDraftDetailBoard`,把 D1-D5 拆成五列詳情層。 + - 每列顯示必要欄位 `6 欄`、禁止事項 `10 禁`、證據格式 `脫敏 refs`,並維持 `待負責人確認`。 + - 邊界顯示 `request_sent=false`、`owner_response_received_count=0`、`owner_response_accepted_count=0`、`secret_plaintext_collection_allowed=false`、`active_runtime_gate_count=0`。 +- `apps/web/messages/zh-TW.json` / `en.json`: + - 新增 `iwooos.s49RequestDraftDetail` 文案;英文語系維持繁中文案鏡像。 +- `docs/security/iwooos-posture-projection.snapshot.json`: + - 新增 `s4_9_request_draft_detail_*` summary 與五筆詳情列機器證據。 + - 鎖定 `required_field_count=6`、`forbidden_action_count=10`、`redacted_evidence_refs_only=true`、`runtime_execution_authorized=false`。 +- `docs/security/gitea-inventory-owner-attestation-request-draft.snapshot.json`: + - 新增 `frontstage_detail_visible=true`、`frontstage_detail_row_count=5`、`frontstage_required_field_total=30`、`frontstage_forbidden_action_count=10`。 +- `docs/security/security-mirror-status-rollup.snapshot.json`: + - 新增 S2.161 台帳,標記這是 framework detail,不是 headline percent 或 runtime delta。 +- `scripts/security/security-mirror-progress-guard.py`: + - 新增 S4.9 詳情層 guard,防止詳情列被誤讀成已送出、已收件、已接受、已審批、已收機密或已開執行期 gate。 + +**驗證**: + +- `python3 -m json.tool apps/web/messages/zh-TW.json` +- `python3 -m json.tool apps/web/messages/en.json` +- `cmp -s apps/web/messages/zh-TW.json apps/web/messages/en.json` +- `python3 -m json.tool docs/security/iwooos-posture-projection.snapshot.json` +- `python3 -m json.tool docs/security/security-mirror-status-rollup.snapshot.json` +- `python3 -m json.tool docs/security/gitea-inventory-owner-attestation-request-draft.snapshot.json` +- `git diff --check` +- `python3 scripts/security/source-control-owner-response-guard.py --root .` → `SOURCE_CONTROL_OWNER_RESPONSE_GUARD_OK` +- `python3 scripts/security/security-mirror-progress-guard.py --root .` → `SECURITY_MIRROR_PROGRESS_GUARD_OK` +- `pnpm --dir apps/web exec tsc --noEmit --tsBuildInfoFile /tmp/iwooos-s49-detail-layer-20260601.tsbuildinfo` +- `NEXT_PUBLIC_API_URL=https://awoooi.wooo.work NEXT_PRIVATE_BUILD_WORKER_COUNT=1 pnpm --dir apps/web run build` +- Browser / Playwright local visual verification: + - Desktop `1440x1100`:S4.9 詳情層存在、五列題目存在、必要邊界字串存在、內部對話語氣不存在、horizontal overflow `0`。 + - Mobile `390x844`:S4.9 詳情層存在、五列題目存在、必要邊界字串存在、內部對話語氣不存在、horizontal overflow `0`。 + +**目前整體進度(本階段完成後)**: + +- 完整 IwoooS / 資安網總進度:維持 `61%`;本階段讓 S4.9 補件草稿更可執行,但仍未收到負責人回覆。 +- 框架 / 治理 / 文件 / schema / read-only evidence:約 `86-88%`;S4.9 題目、欄位、禁區與證據格式已能被前台、snapshot 與 guard 同步看見。 +- Runtime ingestion / GitHub primary / AwoooP production landing:約 `40-45%`;`request_sent_count=0`、`owner_response_received_count=0`、`owner_response_accepted_count=0`、`active_runtime_gate_count=0`。 +- Kali `192.168.0.112` 與開發主機 `192.168.0.111` / `192.168.0.168` 仍維持已納入框架、未啟動掃描 / 修復 / 更新 / 重啟的邊界。 + ## 2026-06-01|ADR-100 observe-only PlayBook 補救工作項落地 **背景**: diff --git a/docs/security/gitea-inventory-owner-attestation-request-draft.snapshot.json b/docs/security/gitea-inventory-owner-attestation-request-draft.snapshot.json index 47c2385d..878ad92c 100644 --- a/docs/security/gitea-inventory-owner-attestation-request-draft.snapshot.json +++ b/docs/security/gitea-inventory-owner-attestation-request-draft.snapshot.json @@ -13,6 +13,10 @@ "request_draft_template_ready_count": 5, "frontstage_package_visible": true, "frontstage_card_count": 5, + "frontstage_detail_visible": true, + "frontstage_detail_row_count": 5, + "frontstage_required_field_total": 30, + "frontstage_forbidden_action_count": 10, "request_sent": false, "request_sent_count": 0, "recipients_confirmed_count": 0, diff --git a/docs/security/iwooos-posture-projection.snapshot.json b/docs/security/iwooos-posture-projection.snapshot.json index ba6d8de1..091e3648 100644 --- a/docs/security/iwooos-posture-projection.snapshot.json +++ b/docs/security/iwooos-posture-projection.snapshot.json @@ -70,6 +70,14 @@ "s4_9_request_draft_package_owner_response_received_count": 0, "s4_9_request_draft_package_owner_response_accepted_count": 0, "s4_9_request_draft_package_runtime_gate_count": 0, + "s4_9_request_draft_detail_first_layer": true, + "s4_9_request_draft_detail_row_count": 5, + "s4_9_request_draft_detail_required_field_total": 30, + "s4_9_request_draft_detail_forbidden_action_count": 10, + "s4_9_request_draft_detail_request_sent_count": 0, + "s4_9_request_draft_detail_owner_response_received_count": 0, + "s4_9_request_draft_detail_owner_response_accepted_count": 0, + "s4_9_request_draft_detail_runtime_gate_count": 0, "visual_command_dashboard_widget_count": 14, "visual_command_dashboard_first_layer": true, "visual_command_dashboard_default_visible": false, @@ -577,6 +585,103 @@ "not_authorization": true } ], + "s4_9_request_draft_detail_rows": [ + { + "row_id": "publicGap", + "display_order": 1, + "source_template_id": "response-public-only-vs-local-gitea-gap", + "template_label": "D1", + "display_mode": "first_screen_s4_9_request_draft_detail", + "draft_status": "ready_not_sent", + "required_field_count": 6, + "forbidden_action_count": 10, + "redacted_evidence_refs_only": true, + "secret_plaintext_collection_allowed": false, + "request_sent_count": 0, + "owner_response_received_count": 0, + "owner_response_accepted_count": 0, + "runtime_gate_count": 0, + "runtime_execution_authorized": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "row_id": "namespaceIdentity", + "display_order": 2, + "source_template_id": "response-org-user-endpoint-identity", + "template_label": "D2", + "display_mode": "first_screen_s4_9_request_draft_detail", + "draft_status": "ready_not_sent", + "required_field_count": 6, + "forbidden_action_count": 10, + "redacted_evidence_refs_only": true, + "secret_plaintext_collection_allowed": false, + "request_sent_count": 0, + "owner_response_received_count": 0, + "owner_response_accepted_count": 0, + "runtime_gate_count": 0, + "runtime_execution_authorized": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "row_id": "adjacentScope", + "display_order": 3, + "source_template_id": "response-internal-110-adjacent-scope", + "template_label": "D3", + "display_mode": "first_screen_s4_9_request_draft_detail", + "draft_status": "ready_not_sent", + "required_field_count": 6, + "forbidden_action_count": 10, + "redacted_evidence_refs_only": true, + "secret_plaintext_collection_allowed": false, + "request_sent_count": 0, + "owner_response_received_count": 0, + "owner_response_accepted_count": 0, + "runtime_gate_count": 0, + "runtime_execution_authorized": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "row_id": "canonicalOwner", + "display_order": 4, + "source_template_id": "response-repo-owner-canonical-scope", + "template_label": "D4", + "display_mode": "first_screen_s4_9_request_draft_detail", + "draft_status": "ready_not_sent", + "required_field_count": 6, + "forbidden_action_count": 10, + "redacted_evidence_refs_only": true, + "secret_plaintext_collection_allowed": false, + "request_sent_count": 0, + "owner_response_received_count": 0, + "owner_response_accepted_count": 0, + "runtime_gate_count": 0, + "runtime_execution_authorized": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "row_id": "legacyDisposition", + "display_order": 5, + "source_template_id": "response-legacy-or-inaccessible-disposition", + "template_label": "D5", + "display_mode": "first_screen_s4_9_request_draft_detail", + "draft_status": "ready_not_sent", + "required_field_count": 6, + "forbidden_action_count": 10, + "redacted_evidence_refs_only": true, + "secret_plaintext_collection_allowed": false, + "request_sent_count": 0, + "owner_response_received_count": 0, + "owner_response_accepted_count": 0, + "runtime_gate_count": 0, + "runtime_execution_authorized": false, + "action_buttons_allowed": 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 4bde37d0..5750e4a8 100644 --- a/docs/security/security-mirror-status-rollup.snapshot.json +++ b/docs/security/security-mirror-status-rollup.snapshot.json @@ -2401,6 +2401,18 @@ "runtime_delta": false, "execution_authorized": false, "not_authorization": true + }, + { + "delta_id": "s2_161_iwooos_s49_request_draft_detail_layer", + "display_order": 190, + "completed_stage": "S2.161 IwoooS S4.9 補件草稿詳情層", + "progress_axis": "framework_detail", + "headline_percent_delta": 0, + "framework_delta_visible": true, + "why_headline_unchanged": "IwoooS 只把 S4.9 五個補件題目拆成前台可讀詳情層,顯示每題 required field count、forbidden action count 與 redacted evidence refs only;s4_9_owner_attestation_request_draft_detail_frontstage_row_count=5、s4_9_owner_attestation_request_draft_detail_required_field_total=30、s4_9_owner_attestation_request_draft_detail_forbidden_action_count=10、s4_9_owner_attestation_request_sent=false、s4_9_owner_attestation_owner_response_received_count=0、s4_9_owner_attestation_owner_response_accepted_count=0、secret_plaintext_collection_allowed=false、runtime_execution_authorized=false、active_runtime_gate_count=0、action_buttons_allowed=false,不把詳情層當請求送出、負責人回覆收到、審查接受、審批紀錄、掃描、修復、部署、主機更新、GitHub primary 切換或 Gitea 停用。", + "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 dc7ec640..6c1d17dd 100755 --- a/scripts/security/security-mirror-progress-guard.py +++ b/scripts/security/security-mirror-progress-guard.py @@ -750,6 +750,7 @@ def validate(root: Path) -> None: "s2_158_iwooos_evidence_unlock_queue", "s2_159_iwooos_s49_request_draft_package", "s2_160_iwooos_s49_request_draft_frontstage_radar", + "s2_161_iwooos_s49_request_draft_detail_layer", ] assert_equal( "progress_delta_ledger.delta_ids", @@ -1764,6 +1765,36 @@ def validate(root: Path) -> None: iwooos_projection["summary"][count_key], 0, ) + assert_true( + "iwooos_projection.summary.s4_9_request_draft_detail_first_layer", + iwooos_projection["summary"]["s4_9_request_draft_detail_first_layer"], + ) + assert_equal( + "iwooos_projection.summary.s4_9_request_draft_detail_row_count", + iwooos_projection["summary"]["s4_9_request_draft_detail_row_count"], + 5, + ) + assert_equal( + "iwooos_projection.summary.s4_9_request_draft_detail_required_field_total", + iwooos_projection["summary"]["s4_9_request_draft_detail_required_field_total"], + 30, + ) + assert_equal( + "iwooos_projection.summary.s4_9_request_draft_detail_forbidden_action_count", + iwooos_projection["summary"]["s4_9_request_draft_detail_forbidden_action_count"], + 10, + ) + for count_key in [ + "s4_9_request_draft_detail_request_sent_count", + "s4_9_request_draft_detail_owner_response_received_count", + "s4_9_request_draft_detail_owner_response_accepted_count", + "s4_9_request_draft_detail_runtime_gate_count", + ]: + assert_equal( + f"iwooos_projection.summary.{count_key}", + iwooos_projection["summary"][count_key], + 0, + ) assert_equal( "iwooos_projection.summary.all_product_coverage_snapshot_default_summary_mode", iwooos_projection["summary"]["all_product_coverage_snapshot_default_summary_mode"], @@ -3344,6 +3375,25 @@ def validate(root: Path) -> None: s49_request_draft["summary"]["frontstage_card_count"], len(expected_s4_9_owner_response_request_template_ids), ) + assert_true( + "s49_request_draft.summary.frontstage_detail_visible", + s49_request_draft["summary"]["frontstage_detail_visible"], + ) + assert_equal( + "s49_request_draft.summary.frontstage_detail_row_count", + s49_request_draft["summary"]["frontstage_detail_row_count"], + len(expected_s4_9_owner_response_request_template_ids), + ) + assert_equal( + "s49_request_draft.summary.frontstage_required_field_total", + s49_request_draft["summary"]["frontstage_required_field_total"], + 30, + ) + assert_equal( + "s49_request_draft.summary.frontstage_forbidden_action_count", + s49_request_draft["summary"]["frontstage_forbidden_action_count"], + 10, + ) assert_false("s49_request_draft.summary.request_sent", s49_request_draft["summary"]["request_sent"]) assert_equal("s49_request_draft.summary.request_sent_count", s49_request_draft["summary"]["request_sent_count"], 0) assert_equal( @@ -12895,6 +12945,79 @@ def validate(root: Path) -> None: f"iwooos_projection.s4_9_request_draft_package_items.{item['item_id']}.not_authorization", item["not_authorization"], ) + s4_9_request_draft_detail_rows = iwooos_projection["s4_9_request_draft_detail_rows"] + assert_equal( + "iwooos_projection.s4_9_request_draft_detail_rows.ids", + [item["row_id"] for item in s4_9_request_draft_detail_rows], + expected_s4_9_request_draft_package_item_ids, + ) + assert_equal( + "iwooos_projection.s4_9_request_draft_detail_rows.display_order", + [item["display_order"] for item in s4_9_request_draft_detail_rows], + list(range(1, len(expected_s4_9_request_draft_package_item_ids) + 1)), + ) + assert_equal( + "iwooos_projection.s4_9_request_draft_detail_rows.source_template_ids", + [item["source_template_id"] for item in s4_9_request_draft_detail_rows], + expected_s4_9_owner_response_request_template_ids, + ) + assert_equal( + "iwooos_projection.s4_9_request_draft_detail_rows.template_labels", + [item["template_label"] for item in s4_9_request_draft_detail_rows], + ["D1", "D2", "D3", "D4", "D5"], + ) + for item in s4_9_request_draft_detail_rows: + assert_equal( + f"iwooos_projection.s4_9_request_draft_detail_rows.{item['row_id']}.display_mode", + item["display_mode"], + "first_screen_s4_9_request_draft_detail", + ) + assert_equal( + f"iwooos_projection.s4_9_request_draft_detail_rows.{item['row_id']}.draft_status", + item["draft_status"], + "ready_not_sent", + ) + assert_equal( + f"iwooos_projection.s4_9_request_draft_detail_rows.{item['row_id']}.required_field_count", + item["required_field_count"], + 6, + ) + assert_equal( + f"iwooos_projection.s4_9_request_draft_detail_rows.{item['row_id']}.forbidden_action_count", + item["forbidden_action_count"], + 10, + ) + assert_true( + f"iwooos_projection.s4_9_request_draft_detail_rows.{item['row_id']}.redacted_evidence_refs_only", + item["redacted_evidence_refs_only"], + ) + assert_false( + f"iwooos_projection.s4_9_request_draft_detail_rows.{item['row_id']}.secret_plaintext_collection_allowed", + item["secret_plaintext_collection_allowed"], + ) + for count_key in [ + "request_sent_count", + "owner_response_received_count", + "owner_response_accepted_count", + "runtime_gate_count", + ]: + assert_equal( + f"iwooos_projection.s4_9_request_draft_detail_rows.{item['row_id']}.{count_key}", + item[count_key], + 0, + ) + assert_false( + f"iwooos_projection.s4_9_request_draft_detail_rows.{item['row_id']}.runtime_execution_authorized", + item["runtime_execution_authorized"], + ) + assert_false( + f"iwooos_projection.s4_9_request_draft_detail_rows.{item['row_id']}.action_buttons_allowed", + item["action_buttons_allowed"], + ) + assert_true( + f"iwooos_projection.s4_9_request_draft_detail_rows.{item['row_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"', @@ -13073,6 +13196,68 @@ def validate(root: Path) -> None: iwooos_projection_page, text, ) + for text in [ + 'data-testid="iwooos-s49-request-draft-detail-board"', + 'data-testid="iwooos-s49-request-draft-detail-items"', + 'data-testid="iwooos-s49-request-draft-detail-boundaries"', + "IwoooSS49RequestDraftDetailBoard", + "s49RequestDraftDetailItems", + "s49RequestDraftDetailBoundaries", + "s49RequestDraftDetail", + "requiredCount", + "forbiddenCount", + "redactedRefs", + "secret_plaintext_collection_allowed=false", + ]: + assert_text_contains( + "iwooos_page.s49_request_draft_detail", + iwooos_projection_page, + text, + ) + for text in [ + "publicGap", + "namespaceIdentity", + "adjacentScope", + "canonicalOwner", + "legacyDisposition", + ]: + assert_text_contains( + "iwooos_page.s49_request_draft_detail_items", + iwooos_projection_page, + text, + ) + assert_text_before( + "iwooos_page.s49_request_draft_package_before_detail", + iwooos_projection_page, + "", + "", + ) + assert_text_before( + "iwooos_page.s49_request_draft_detail_before_immediate_visual_mesh", + iwooos_projection_page, + "", + "", + ) + for text in [ + "s4_9_owner_attestation_request_draft_detail_frontstage_row_count=5", + "s4_9_owner_attestation_request_draft_detail_required_field_total=30", + "s4_9_owner_attestation_request_draft_detail_forbidden_action_count=10", + "s4_9_owner_attestation_request_sent=false", + "s4_9_owner_attestation_owner_response_received_count=0", + "s4_9_owner_attestation_owner_response_accepted_count=0", + "s4_9_owner_attestation_runtime_gate_opened=false", + "redacted_evidence_refs_only=true", + "secret_plaintext_collection_allowed=false", + "runtime_execution_authorized=false", + "active_runtime_gate_count=0", + "action_buttons_allowed=false", + "not_authorization=true", + ]: + assert_text_contains( + "iwooos_page.s49_request_draft_detail_boundary", + iwooos_projection_page, + text, + ) assert_text_contains( "iwooos_page.focus_deck_testid", iwooos_projection_page,