diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json
index d92e037c7..0d019bcf5 100644
--- a/apps/web/messages/en.json
+++ b/apps/web/messages/en.json
@@ -2655,6 +2655,59 @@
"guard": "not_authorization=true; approval record=false"
}
}
+ },
+ "hostOwnerDecisionRecordHumanRecordOwnerReviewPreparationChecklist": {
+ "title": "Host Owner Decision Record Human Record Owner Review Preparation Checklist",
+ "subtitle": "Human record owner review preparation checklist only checks whether preparation packets are readable. It does not mark preparation completed, mark checklist passed, start review, mark review ready, collect owner decisions, create decision records, create approval records, or open runtime gates.",
+ "checkLabel": "Review preparation check",
+ "guardLabel": "Guardrail",
+ "items": {
+ "preparationIdentityTraceReadable": {
+ "title": "Preparation identity trace readable",
+ "body": "Checks that preparation id, source candidate outcome, version, trace pointer, and candidate checklist link are traceable.",
+ "guard": "prepared=0; check passed=0"
+ },
+ "preparationOwnerBoundaryReadable": {
+ "title": "Preparation owner boundary readable",
+ "body": "Checks that human record owner, backup owner, contact channel, responsibility boundary, and open clarifications are readable without treating the owner as engaged.",
+ "guard": "owner decision received=0; review ready=0"
+ },
+ "preparationDecisionSummaryReadable": {
+ "title": "Preparation decision summary readable",
+ "body": "Checks that candidate decision summary, risk acceptance boundary, no-execution statement, and formal record preface are readable while remaining outside a decision record.",
+ "guard": "decision record created=false; accepted=0"
+ },
+ "preparationScopeExpiryCurrent": {
+ "title": "Preparation scope and expiry current",
+ "body": "Checks that host, network, service, exclusion, observation intent, expiry, and refresh need remain readable within the preparation layer.",
+ "guard": "scope check only; runtime gate opened=false"
+ },
+ "preparationScanLimitsNotAuthorization": {
+ "title": "Preparation scan limits not authorization",
+ "body": "Checks that observe-only, future active scan, and credentialed scan limits are not written as scan authorization.",
+ "guard": "scan authorized=false; action buttons=false"
+ },
+ "preparationCredentialBoundaryMetadataOnly": {
+ "title": "Preparation credential boundary metadata-only",
+ "body": "Checks that credential owner, retention, masking, forbidden collection, and quarantine rules remain metadata-only.",
+ "guard": "secret collection=false; raw payload=false"
+ },
+ "preparationMaintenanceRollbackTraceable": {
+ "title": "Preparation maintenance and rollback traceable",
+ "body": "Checks that maintenance window, constraints, rollback owner, recovery path, and human contact are traceable.",
+ "guard": "host change=false; Kali update=false"
+ },
+ "preparationValidationRuntimeGateSeparate": {
+ "title": "Preparation validation and runtime gate separate",
+ "body": "Checks that validation evidence pointer, post-check metrics, and independent runtime gate requirement remain separate.",
+ "guard": "runtime gate opened=false; runtime execution=false"
+ },
+ "preparationNoExecutionAttestationPresent": {
+ "title": "Preparation no-execution attestation present",
+ "body": "Checks that not authorization, no execution, no approval, and no runtime gate statements are visible so the checklist is not mistaken for approval.",
+ "guard": "not_authorization=true; approval record=false"
+ }
+ }
}
},
"tickets": {
diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json
index b51d6d2d4..cfea5bb7c 100644
--- a/apps/web/messages/zh-TW.json
+++ b/apps/web/messages/zh-TW.json
@@ -2656,6 +2656,59 @@
"guard": "not_authorization=true;approval record=false"
}
}
+ },
+ "hostOwnerDecisionRecordHumanRecordOwnerReviewPreparationChecklist": {
+ "title": "主機 Owner Decision Record Human Record Owner Review Preparation Checklist",
+ "subtitle": "Human record owner review preparation checklist 只核對 preparation packets 是否可讀。不代表 preparation completed、不代表 checklist passed、不開始 review、不標記 review ready、不收 owner decision、不建立 decision record、不建立 approval record、不開 runtime gate。",
+ "checkLabel": "Review preparation check",
+ "guardLabel": "保護邊界",
+ "items": {
+ "preparationIdentityTraceReadable": {
+ "title": "Preparation identity trace readable",
+ "body": "確認 preparation id、來源 candidate outcome、版本、trace pointer 與 candidate checklist link 可追溯。",
+ "guard": "prepared=0;check passed=0"
+ },
+ "preparationOwnerBoundaryReadable": {
+ "title": "Preparation owner boundary readable",
+ "body": "確認 human record owner、backup owner、聯絡窗口、責任邊界與待確認項目可讀,但不代表 owner 已接案。",
+ "guard": "owner decision received=0;review ready=0"
+ },
+ "preparationDecisionSummaryReadable": {
+ "title": "Preparation decision summary readable",
+ "body": "確認候選決策摘要、風險接受邊界、no-execution statement 與正式紀錄前置說明可讀,仍不是 decision record。",
+ "guard": "decision record created=false;accepted=0"
+ },
+ "preparationScopeExpiryCurrent": {
+ "title": "Preparation scope and expiry current",
+ "body": "確認 host、network、service、exclusion、observation intent、expiry 與刷新需求仍在準備層可讀範圍內。",
+ "guard": "scope check only;runtime gate opened=false"
+ },
+ "preparationScanLimitsNotAuthorization": {
+ "title": "Preparation scan limits not authorization",
+ "body": "確認 observe-only、future active scan 與 credentialed scan limits 沒有被寫成掃描授權。",
+ "guard": "scan authorized=false;action buttons=false"
+ },
+ "preparationCredentialBoundaryMetadataOnly": {
+ "title": "Preparation credential boundary metadata-only",
+ "body": "確認 credential owner、retention、masking、forbidden collection 與隔離規則仍是 metadata-only。",
+ "guard": "secret collection=false;raw payload=false"
+ },
+ "preparationMaintenanceRollbackTraceable": {
+ "title": "Preparation maintenance and rollback traceable",
+ "body": "確認 maintenance window、constraints、rollback owner、recovery path 與人工聯絡點可追溯。",
+ "guard": "host change=false;Kali update=false"
+ },
+ "preparationValidationRuntimeGateSeparate": {
+ "title": "Preparation validation and runtime gate separate",
+ "body": "確認 validation evidence pointer、post-check metrics 與獨立 runtime gate requirement 仍維持分離。",
+ "guard": "runtime gate opened=false;runtime execution=false"
+ },
+ "preparationNoExecutionAttestationPresent": {
+ "title": "Preparation no-execution attestation present",
+ "body": "確認 not authorization、no execution、no approval、no runtime gate statement 都可見,避免 checklist 被誤解成批准。",
+ "guard": "not_authorization=true;approval record=false"
+ }
+ }
}
},
"tickets": {
diff --git a/apps/web/src/app/[locale]/iwooos/page.tsx b/apps/web/src/app/[locale]/iwooos/page.tsx
index 05235d820..e8bc73eb8 100644
--- a/apps/web/src/app/[locale]/iwooos/page.tsx
+++ b/apps/web/src/app/[locale]/iwooos/page.tsx
@@ -292,6 +292,13 @@ type HostOwnerDecisionRecordHumanRecordOwnerReviewPreparationPacket = {
tone: 'steady' | 'warn' | 'locked'
}
+type HostOwnerDecisionRecordHumanRecordOwnerReviewPreparationChecklistItem = {
+ key: string
+ check: string
+ icon: typeof ShieldCheck
+ tone: 'steady' | 'warn' | 'locked'
+}
+
const postureMetrics: PostureMetric[] = [
{ key: 'overall', value: '58%', tone: 'warn' },
{ key: 'framework', value: '80-85%', tone: 'steady' },
@@ -709,6 +716,18 @@ const hostOwnerDecisionRecordHumanRecordOwnerReviewPreparationPackets: HostOwner
{ key: 'preparationNoExecutionAttestation', packet: 'FRP9', icon: ListChecks, tone: 'locked' },
]
+const hostOwnerDecisionRecordHumanRecordOwnerReviewPreparationChecklistItems: HostOwnerDecisionRecordHumanRecordOwnerReviewPreparationChecklistItem[] = [
+ { key: 'preparationIdentityTraceReadable', check: 'FRPC1', icon: FileText, tone: 'warn' },
+ { key: 'preparationOwnerBoundaryReadable', check: 'FRPC2', icon: Bell, tone: 'warn' },
+ { key: 'preparationDecisionSummaryReadable', check: 'FRPC3', icon: ClipboardCheck, tone: 'warn' },
+ { key: 'preparationScopeExpiryCurrent', check: 'FRPC4', icon: Radar, tone: 'warn' },
+ { key: 'preparationScanLimitsNotAuthorization', check: 'FRPC5', icon: Activity, tone: 'locked' },
+ { key: 'preparationCredentialBoundaryMetadataOnly', check: 'FRPC6', icon: Lock, tone: 'locked' },
+ { key: 'preparationMaintenanceRollbackTraceable', check: 'FRPC7', icon: Clock3, tone: 'warn' },
+ { key: 'preparationValidationRuntimeGateSeparate', check: 'FRPC8', icon: ShieldCheck, tone: 'locked' },
+ { key: 'preparationNoExecutionAttestationPresent', check: 'FRPC9', icon: ListChecks, tone: 'locked' },
+]
+
const evidenceItems = [
'iwooos-posture-projection.snapshot.json',
'security-rollout-policy.snapshot.json',
@@ -1883,6 +1902,38 @@ function HostOwnerDecisionRecordHumanRecordOwnerReviewPreparationPacketCard({
)
}
+function HostOwnerDecisionRecordHumanRecordOwnerReviewPreparationChecklistCard({
+ item,
+}: {
+ item: HostOwnerDecisionRecordHumanRecordOwnerReviewPreparationChecklistItem
+}) {
+ const t = useTranslations('iwooos.hostOwnerDecisionRecordHumanRecordOwnerReviewPreparationChecklist')
+ const Icon = item.icon
+ return (
+
+
+
+
+ {t('checkLabel')}
+
+
{item.check}
+
+
+ {t(`items.${item.key}.title` as never)}
+
+
+ {t(`items.${item.key}.body` as never)}
+
+
+
{t('guardLabel')}
+
+ {t(`items.${item.key}.guard` as never)}
+
+
+
+ )
+}
+
export default function IwoooSPage({ params }: { params: { locale: string } }) {
const t = useTranslations('iwooos')
@@ -2585,6 +2636,28 @@ export default function IwoooSPage({ params }: { params: { locale: string } }) {
+
+
+
+ {t('hostOwnerDecisionRecordHumanRecordOwnerReviewPreparationChecklist.title')}
+
+
+ {t('hostOwnerDecisionRecordHumanRecordOwnerReviewPreparationChecklist.subtitle')}
+
+
+
+ {hostOwnerDecisionRecordHumanRecordOwnerReviewPreparationChecklistItems.map(item => (
+
+ ))}
+
+
+
None:
"s2_42_iwooos_host_owner_decision_record_human_record_owner_review_candidate_checklist",
"s2_43_iwooos_host_owner_decision_record_human_record_owner_review_candidate_outcome_lanes",
"s2_44_iwooos_host_owner_decision_record_human_record_owner_review_preparation_packets",
+ "s2_45_iwooos_host_owner_decision_record_human_record_owner_review_preparation_checklist",
]
assert_equal(
"progress_delta_ledger.delta_ids",
@@ -654,6 +655,17 @@ def validate(root: Path) -> None:
"host_decision_record_human_record_owner_review_preparation_validation_runtime_gate_packet",
"host_decision_record_human_record_owner_review_preparation_no_execution_attestation_packet",
]
+ expected_iwooos_host_owner_decision_record_human_record_owner_review_preparation_checklist_item_ids = [
+ "host_decision_record_human_record_owner_review_preparation_identity_trace_readable_check",
+ "host_decision_record_human_record_owner_review_preparation_owner_boundary_readable_check",
+ "host_decision_record_human_record_owner_review_preparation_decision_summary_readable_check",
+ "host_decision_record_human_record_owner_review_preparation_scope_expiry_current_check",
+ "host_decision_record_human_record_owner_review_preparation_scan_limits_not_authorization_check",
+ "host_decision_record_human_record_owner_review_preparation_credential_boundary_metadata_only_check",
+ "host_decision_record_human_record_owner_review_preparation_maintenance_rollback_traceable_check",
+ "host_decision_record_human_record_owner_review_preparation_validation_runtime_gate_separate_check",
+ "host_decision_record_human_record_owner_review_preparation_no_execution_attestation_present_check",
+ ]
assert_equal(
"iwooos_projection.summary.frontend_surface_coverage_group_count",
iwooos_projection["summary"]["frontend_surface_coverage_group_count"],
@@ -833,6 +845,13 @@ def validate(root: Path) -> None:
iwooos_projection["summary"]["host_owner_decision_record_human_record_owner_review_preparation_packet_count"],
len(expected_iwooos_host_owner_decision_record_human_record_owner_review_preparation_packet_ids),
)
+ assert_equal(
+ "iwooos_projection.summary.host_owner_decision_record_human_record_owner_review_preparation_checklist_item_count",
+ iwooos_projection["summary"][
+ "host_owner_decision_record_human_record_owner_review_preparation_checklist_item_count"
+ ],
+ len(expected_iwooos_host_owner_decision_record_human_record_owner_review_preparation_checklist_item_ids),
+ )
iwooos_progress = iwooos_projection["progress"]
assert_equal("iwooos_projection.progress.overall_percent", iwooos_progress["overall_percent"], progress["overall_percent"])
assert_equal(
@@ -3631,6 +3650,169 @@ def validate(root: Path) -> None:
f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_packets.{item['packet_id']}.not_authorization",
item["not_authorization"],
)
+ iwooos_host_owner_decision_record_human_record_owner_review_preparation_checklist_items = iwooos_projection[
+ "host_owner_decision_record_human_record_owner_review_preparation_checklist_items"
+ ]
+ assert_equal(
+ "iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.ids",
+ [
+ item["item_id"]
+ for item in iwooos_host_owner_decision_record_human_record_owner_review_preparation_checklist_items
+ ],
+ expected_iwooos_host_owner_decision_record_human_record_owner_review_preparation_checklist_item_ids,
+ )
+ assert_equal(
+ "iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.display_order",
+ [
+ item["display_order"]
+ for item in iwooos_host_owner_decision_record_human_record_owner_review_preparation_checklist_items
+ ],
+ list(
+ range(
+ 1,
+ len(expected_iwooos_host_owner_decision_record_human_record_owner_review_preparation_checklist_item_ids) + 1,
+ )
+ ),
+ )
+ expected_iwooos_host_owner_decision_record_human_record_owner_review_preparation_review_conditions = [
+ "preparation_identity_trace_readable",
+ "preparation_owner_boundary_readable",
+ "preparation_decision_summary_readable",
+ "preparation_scope_expiry_current",
+ "preparation_scan_limits_not_authorization",
+ "preparation_credential_boundary_metadata_only",
+ "preparation_maintenance_rollback_traceable",
+ "preparation_validation_runtime_gate_separate",
+ "preparation_no_execution_attestation_present",
+ ]
+ assert_equal(
+ "iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.review_conditions",
+ [
+ item["review_condition"]
+ for item in iwooos_host_owner_decision_record_human_record_owner_review_preparation_checklist_items
+ ],
+ expected_iwooos_host_owner_decision_record_human_record_owner_review_preparation_review_conditions,
+ )
+ expected_iwooos_host_owner_decision_record_human_record_owner_review_preparation_failure_routes = [
+ "refresh_preparation_identity_trace",
+ "clarify_preparation_owner_boundary",
+ "clarify_preparation_decision_summary",
+ "refresh_preparation_scope_expiry",
+ "clarify_preparation_scan_limits",
+ "quarantine_preparation_credential_boundary",
+ "complete_preparation_maintenance_rollback",
+ "keep_runtime_gate_separate",
+ "keep_no_execution_attestation_visible",
+ ]
+ assert_equal(
+ "iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.failure_routes",
+ [
+ item["failure_route"]
+ for item in iwooos_host_owner_decision_record_human_record_owner_review_preparation_checklist_items
+ ],
+ expected_iwooos_host_owner_decision_record_human_record_owner_review_preparation_failure_routes,
+ )
+ assert_equal(
+ "iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.source_preparation_packet_ids",
+ [
+ item["source_preparation_packet_id"]
+ for item in iwooos_host_owner_decision_record_human_record_owner_review_preparation_checklist_items
+ ],
+ expected_iwooos_host_owner_decision_record_human_record_owner_review_preparation_packet_ids,
+ )
+ for item in iwooos_host_owner_decision_record_human_record_owner_review_preparation_checklist_items:
+ assert_equal(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.{item['item_id']}.display_mode",
+ item["display_mode"],
+ "owner_decision_record_human_record_owner_review_preparation_checklist_only",
+ )
+ assert_equal(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.{item['item_id']}.human_record_owner_review_prepared_count",
+ item["human_record_owner_review_prepared_count"],
+ 0,
+ )
+ assert_equal(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.{item['item_id']}.human_record_owner_review_check_passed_count",
+ item["human_record_owner_review_check_passed_count"],
+ 0,
+ )
+ assert_equal(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.{item['item_id']}.human_record_owner_review_started_count",
+ item["human_record_owner_review_started_count"],
+ 0,
+ )
+ assert_equal(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.{item['item_id']}.human_record_owner_review_ready_count",
+ item["human_record_owner_review_ready_count"],
+ 0,
+ )
+ assert_equal(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.{item['item_id']}.human_record_owner_handoff_review_passed_count",
+ item["human_record_owner_handoff_review_passed_count"],
+ 0,
+ )
+ assert_equal(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.{item['item_id']}.human_record_owner_handoff_started_count",
+ item["human_record_owner_handoff_started_count"],
+ 0,
+ )
+ assert_equal(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.{item['item_id']}.human_record_owner_handoff_ready_count",
+ item["human_record_owner_handoff_ready_count"],
+ 0,
+ )
+ assert_equal(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.{item['item_id']}.formal_record_queue_review_passed_count",
+ item["formal_record_queue_review_passed_count"],
+ 0,
+ )
+ assert_equal(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.{item['item_id']}.formal_record_queue_enqueued_count",
+ item["formal_record_queue_enqueued_count"],
+ 0,
+ )
+ assert_false(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.{item['item_id']}.decision_record_created",
+ item["decision_record_created"],
+ )
+ assert_equal(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.{item['item_id']}.owner_decision_received_count",
+ item["owner_decision_received_count"],
+ 0,
+ )
+ assert_equal(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.{item['item_id']}.owner_decision_accepted_count",
+ item["owner_decision_accepted_count"],
+ 0,
+ )
+ assert_false(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.{item['item_id']}.owner_approval_record_created",
+ item["owner_approval_record_created"],
+ )
+ assert_false(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.{item['item_id']}.runtime_gate_opened",
+ item["runtime_gate_opened"],
+ )
+ assert_false(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.{item['item_id']}.raw_payload_allowed",
+ item["raw_payload_allowed"],
+ )
+ assert_false(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.{item['item_id']}.secret_value_collection_allowed",
+ item["secret_value_collection_allowed"],
+ )
+ assert_false(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.{item['item_id']}.runtime_execution_authorized",
+ item["runtime_execution_authorized"],
+ )
+ assert_false(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.{item['item_id']}.action_buttons_allowed",
+ item["action_buttons_allowed"],
+ )
+ assert_true(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_preparation_checklist_items.{item['item_id']}.not_authorization",
+ item["not_authorization"],
+ )
assert_equal(
"iwooos_projection.non_blocking_lane_ids",
iwooos_projection["non_blocking_lane_ids"],
@@ -3684,6 +3866,7 @@ def validate(root: Path) -> None:
"display_host_owner_decision_record_human_record_owner_review_candidate_checklist",
"display_host_owner_decision_record_human_record_owner_review_candidate_outcome_lanes",
"display_host_owner_decision_record_human_record_owner_review_preparation_packets",
+ "display_host_owner_decision_record_human_record_owner_review_preparation_checklist",
"display_evidence_refs",
"display_forbidden_actions",
]:
@@ -3831,6 +4014,14 @@ def validate(root: Path) -> None:
"collect_owner_decision_from_human_record_owner_review_preparation_packet",
"create_host_owner_decision_record_from_human_record_owner_review_preparation_packet",
"open_runtime_gate_from_human_record_owner_review_preparation_packet",
+ "treat_host_owner_decision_record_human_record_owner_review_preparation_checklist_as_approval",
+ "mark_human_record_owner_review_preparation_checklist_passed",
+ "mark_human_record_owner_review_prepared_from_preparation_checklist",
+ "start_human_record_owner_review_from_preparation_checklist",
+ "mark_human_record_owner_review_ready_from_preparation_checklist",
+ "collect_owner_decision_from_human_record_owner_review_preparation_checklist",
+ "create_host_owner_decision_record_from_human_record_owner_review_preparation_checklist",
+ "open_runtime_gate_from_human_record_owner_review_preparation_checklist",
"apply_runtime_blocking_control",
"switch_github_primary",
"production_deploy",