diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json
index 5113f618e..3a2e0a316 100644
--- a/apps/web/messages/en.json
+++ b/apps/web/messages/en.json
@@ -2496,6 +2496,59 @@
"guard": "not_authorization=true; approval record=false"
}
}
+ },
+ "hostOwnerDecisionRecordHumanRecordOwnerReviewCandidateChecklist": {
+ "title": "Host Owner Decision Record Human Record Owner Review Candidate Checklist",
+ "subtitle": "Human record owner review candidate checklist only checks whether candidate packets are readable. It does not mark checklist passed, start review, mark review ready, collect owner decisions, create decision records, create approval records, or open runtime gates.",
+ "checkLabel": "Review candidate check",
+ "guardLabel": "Guardrail",
+ "items": {
+ "candidateIdentityTraceable": {
+ "title": "Candidate identity traceable",
+ "body": "Checks that candidate id, source outcome, version, trace pointer, and queue review link are traceable.",
+ "guard": "check passed=0; review started=0"
+ },
+ "candidateOwnerBoundaryReadable": {
+ "title": "Candidate owner boundary readable",
+ "body": "Checks that human record owner, backup owner, contact channel, and responsibility boundary are readable without treating the owner as engaged.",
+ "guard": "owner decision received=0; review ready=0"
+ },
+ "candidateDecisionSummaryReadable": {
+ "title": "Candidate decision summary readable",
+ "body": "Checks that candidate decision summary, risk acceptance boundary, and no-execution statement are readable while remaining outside a formal decision record.",
+ "guard": "decision record created=false; accepted=0"
+ },
+ "candidateScopeExpiryCurrent": {
+ "title": "Candidate scope and expiry current",
+ "body": "Checks that host, network, service, exclusion, observation intent, and expiry remain within the candidate scope.",
+ "guard": "scope check only; runtime gate opened=false"
+ },
+ "candidateScanLimitsNotAuthorization": {
+ "title": "Candidate 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"
+ },
+ "candidateCredentialBoundaryMetadataOnly": {
+ "title": "Candidate credential boundary metadata-only",
+ "body": "Checks that credential owner, retention, masking, and forbidden collection remain metadata-only.",
+ "guard": "secret collection=false; raw payload=false"
+ },
+ "candidateMaintenanceRollbackTraceable": {
+ "title": "Candidate 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"
+ },
+ "candidateValidationRuntimeGateSeparate": {
+ "title": "Candidate validation and runtime gate separate",
+ "body": "Checks that validation evidence pointer, post-check metrics, and runtime gate requirement remain separate.",
+ "guard": "runtime gate opened=false; runtime execution=false"
+ },
+ "candidateNoExecutionAttestationPresent": {
+ "title": "Candidate no-execution attestation present",
+ "body": "Checks that not authorization, no execution, no approval, and no runtime gate statements are visible.",
+ "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 0ee54fa5d..9f0054e00 100644
--- a/apps/web/messages/zh-TW.json
+++ b/apps/web/messages/zh-TW.json
@@ -2497,6 +2497,59 @@
"guard": "not_authorization=true;approval record=false"
}
}
+ },
+ "hostOwnerDecisionRecordHumanRecordOwnerReviewCandidateChecklist": {
+ "title": "主機 Owner Decision Record Human Record Owner Review Candidate Checklist",
+ "subtitle": "Human record owner review candidate checklist 只核對 candidate packets 是否可讀。不代表 checklist passed、不開始 review、不標記 review ready、不收 owner decision、不建立 decision record、不建立 approval record、不開 runtime gate。",
+ "checkLabel": "Review candidate check",
+ "guardLabel": "保護邊界",
+ "items": {
+ "candidateIdentityTraceable": {
+ "title": "Candidate identity traceable",
+ "body": "確認 candidate id、來源 outcome、版本、trace pointer 與 queue review link 可追溯。",
+ "guard": "check passed=0;review started=0"
+ },
+ "candidateOwnerBoundaryReadable": {
+ "title": "Candidate owner boundary readable",
+ "body": "確認 human record owner、backup owner、聯絡窗口與責任邊界可讀,但不代表 owner 已接案。",
+ "guard": "owner decision received=0;review ready=0"
+ },
+ "candidateDecisionSummaryReadable": {
+ "title": "Candidate decision summary readable",
+ "body": "確認候選決策摘要、風險接受邊界與 no-execution statement 可讀,仍不是正式 decision record。",
+ "guard": "decision record created=false;accepted=0"
+ },
+ "candidateScopeExpiryCurrent": {
+ "title": "Candidate scope and expiry current",
+ "body": "確認 host、network、service、exclusion、observation intent 與 expiry 仍在候選範圍內。",
+ "guard": "scope check only;runtime gate opened=false"
+ },
+ "candidateScanLimitsNotAuthorization": {
+ "title": "Candidate scan limits not authorization",
+ "body": "確認 observe-only、future active scan 與 credentialed scan limits 沒有被寫成掃描授權。",
+ "guard": "scan authorized=false;action buttons=false"
+ },
+ "candidateCredentialBoundaryMetadataOnly": {
+ "title": "Candidate credential boundary metadata-only",
+ "body": "確認 credential owner、retention、masking 與 forbidden collection 仍是 metadata-only。",
+ "guard": "secret collection=false;raw payload=false"
+ },
+ "candidateMaintenanceRollbackTraceable": {
+ "title": "Candidate maintenance and rollback traceable",
+ "body": "確認 maintenance window、constraints、rollback owner、recovery path 與人工聯絡點可追溯。",
+ "guard": "host change=false;Kali update=false"
+ },
+ "candidateValidationRuntimeGateSeparate": {
+ "title": "Candidate validation and runtime gate separate",
+ "body": "確認 validation evidence pointer、post-check metrics 與 runtime gate requirement 仍維持獨立。",
+ "guard": "runtime gate opened=false;runtime execution=false"
+ },
+ "candidateNoExecutionAttestationPresent": {
+ "title": "Candidate no-execution attestation present",
+ "body": "確認 not authorization、no execution、no approval、no runtime gate statement 都可見。",
+ "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 f5fd175ad..43bf49606 100644
--- a/apps/web/src/app/[locale]/iwooos/page.tsx
+++ b/apps/web/src/app/[locale]/iwooos/page.tsx
@@ -271,6 +271,13 @@ type HostOwnerDecisionRecordHumanRecordOwnerReviewCandidatePacket = {
tone: 'steady' | 'warn' | 'locked'
}
+type HostOwnerDecisionRecordHumanRecordOwnerReviewCandidateChecklistItem = {
+ 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' },
@@ -652,6 +659,18 @@ const hostOwnerDecisionRecordHumanRecordOwnerReviewCandidatePackets: HostOwnerDe
{ key: 'reviewNoExecutionAttestation', packet: 'FHR9', icon: ListChecks, tone: 'locked' },
]
+const hostOwnerDecisionRecordHumanRecordOwnerReviewCandidateChecklistItems: HostOwnerDecisionRecordHumanRecordOwnerReviewCandidateChecklistItem[] = [
+ { key: 'candidateIdentityTraceable', check: 'FRC1', icon: FileText, tone: 'warn' },
+ { key: 'candidateOwnerBoundaryReadable', check: 'FRC2', icon: Bell, tone: 'warn' },
+ { key: 'candidateDecisionSummaryReadable', check: 'FRC3', icon: ClipboardCheck, tone: 'warn' },
+ { key: 'candidateScopeExpiryCurrent', check: 'FRC4', icon: Radar, tone: 'warn' },
+ { key: 'candidateScanLimitsNotAuthorization', check: 'FRC5', icon: Activity, tone: 'locked' },
+ { key: 'candidateCredentialBoundaryMetadataOnly', check: 'FRC6', icon: Lock, tone: 'locked' },
+ { key: 'candidateMaintenanceRollbackTraceable', check: 'FRC7', icon: Clock3, tone: 'warn' },
+ { key: 'candidateValidationRuntimeGateSeparate', check: 'FRC8', icon: ShieldCheck, tone: 'locked' },
+ { key: 'candidateNoExecutionAttestationPresent', check: 'FRC9', icon: ListChecks, tone: 'locked' },
+]
+
const evidenceItems = [
'iwooos-posture-projection.snapshot.json',
'security-rollout-policy.snapshot.json',
@@ -1730,6 +1749,38 @@ function HostOwnerDecisionRecordHumanRecordOwnerReviewCandidatePacketCard({
)
}
+function HostOwnerDecisionRecordHumanRecordOwnerReviewCandidateChecklistCard({
+ item,
+}: {
+ item: HostOwnerDecisionRecordHumanRecordOwnerReviewCandidateChecklistItem
+}) {
+ const t = useTranslations('iwooos.hostOwnerDecisionRecordHumanRecordOwnerReviewCandidateChecklist')
+ 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')
@@ -2366,6 +2417,28 @@ export default function IwoooSPage({ params }: { params: { locale: string } }) {
+
+
+
+ {t('hostOwnerDecisionRecordHumanRecordOwnerReviewCandidateChecklist.title')}
+
+
+ {t('hostOwnerDecisionRecordHumanRecordOwnerReviewCandidateChecklist.subtitle')}
+
+
+
+ {hostOwnerDecisionRecordHumanRecordOwnerReviewCandidateChecklistItems.map(item => (
+
+ ))}
+
+
+
None:
"s2_39_iwooos_host_owner_decision_record_human_handoff_readiness_review_checklist",
"s2_40_iwooos_host_owner_decision_record_human_handoff_readiness_review_outcome_lanes",
"s2_41_iwooos_host_owner_decision_record_human_record_owner_review_candidate_packets",
+ "s2_42_iwooos_host_owner_decision_record_human_record_owner_review_candidate_checklist",
]
assert_equal(
"progress_delta_ledger.delta_ids",
@@ -618,6 +619,17 @@ def validate(root: Path) -> None:
"host_decision_record_human_record_owner_review_candidate_validation_runtime_gate_packet",
"host_decision_record_human_record_owner_review_candidate_no_execution_attestation_packet",
]
+ expected_iwooos_host_owner_decision_record_human_record_owner_review_candidate_checklist_item_ids = [
+ "host_decision_record_human_record_owner_review_candidate_identity_traceable_check",
+ "host_decision_record_human_record_owner_review_candidate_owner_boundary_readable_check",
+ "host_decision_record_human_record_owner_review_candidate_decision_summary_readable_check",
+ "host_decision_record_human_record_owner_review_candidate_scope_expiry_current_check",
+ "host_decision_record_human_record_owner_review_candidate_scan_limits_not_authorization_check",
+ "host_decision_record_human_record_owner_review_candidate_credential_boundary_metadata_only_check",
+ "host_decision_record_human_record_owner_review_candidate_maintenance_rollback_traceable_check",
+ "host_decision_record_human_record_owner_review_candidate_validation_runtime_gate_separate_check",
+ "host_decision_record_human_record_owner_review_candidate_no_execution_attestation_present_check",
+ ]
assert_equal(
"iwooos_projection.summary.frontend_surface_coverage_group_count",
iwooos_projection["summary"]["frontend_surface_coverage_group_count"],
@@ -778,6 +790,13 @@ def validate(root: Path) -> None:
iwooos_projection["summary"]["host_owner_decision_record_human_record_owner_review_candidate_packet_count"],
len(expected_iwooos_host_owner_decision_record_human_record_owner_review_candidate_packet_ids),
)
+ assert_equal(
+ "iwooos_projection.summary.host_owner_decision_record_human_record_owner_review_candidate_checklist_item_count",
+ iwooos_projection["summary"][
+ "host_owner_decision_record_human_record_owner_review_candidate_checklist_item_count"
+ ],
+ len(expected_iwooos_host_owner_decision_record_human_record_owner_review_candidate_checklist_item_ids),
+ )
iwooos_progress = iwooos_projection["progress"]
assert_equal("iwooos_projection.progress.overall_percent", iwooos_progress["overall_percent"], progress["overall_percent"])
assert_equal(
@@ -3171,6 +3190,138 @@ def validate(root: Path) -> None:
f"iwooos_projection.host_owner_decision_record_human_record_owner_review_candidate_packets.{item['packet_id']}.not_authorization",
item["not_authorization"],
)
+ iwooos_host_owner_decision_record_human_record_owner_review_candidate_checklist_items = iwooos_projection[
+ "host_owner_decision_record_human_record_owner_review_candidate_checklist_items"
+ ]
+ assert_equal(
+ "iwooos_projection.host_owner_decision_record_human_record_owner_review_candidate_checklist_items.ids",
+ [
+ item["item_id"]
+ for item in iwooos_host_owner_decision_record_human_record_owner_review_candidate_checklist_items
+ ],
+ expected_iwooos_host_owner_decision_record_human_record_owner_review_candidate_checklist_item_ids,
+ )
+ assert_equal(
+ "iwooos_projection.host_owner_decision_record_human_record_owner_review_candidate_checklist_items.display_order",
+ [
+ item["display_order"]
+ for item in iwooos_host_owner_decision_record_human_record_owner_review_candidate_checklist_items
+ ],
+ list(
+ range(
+ 1,
+ len(expected_iwooos_host_owner_decision_record_human_record_owner_review_candidate_checklist_item_ids)
+ + 1,
+ )
+ ),
+ )
+ expected_iwooos_host_owner_decision_record_human_record_owner_review_candidate_review_conditions = [
+ "candidate_identity_traceable",
+ "candidate_owner_boundary_readable",
+ "candidate_decision_summary_readable",
+ "candidate_scope_expiry_current",
+ "candidate_scan_limits_not_authorization",
+ "candidate_credential_boundary_metadata_only",
+ "candidate_maintenance_rollback_traceable",
+ "candidate_validation_runtime_gate_separate",
+ "candidate_no_execution_attestation_present",
+ ]
+ assert_equal(
+ "iwooos_projection.host_owner_decision_record_human_record_owner_review_candidate_checklist_items.review_conditions",
+ [
+ item["review_condition"]
+ for item in iwooos_host_owner_decision_record_human_record_owner_review_candidate_checklist_items
+ ],
+ expected_iwooos_host_owner_decision_record_human_record_owner_review_candidate_review_conditions,
+ )
+ for item in iwooos_host_owner_decision_record_human_record_owner_review_candidate_checklist_items:
+ assert_equal(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_candidate_checklist_items.{item['item_id']}.display_mode",
+ item["display_mode"],
+ "owner_decision_record_human_record_owner_review_candidate_checklist_only",
+ )
+ assert_equal(
+ f"iwooos_projection.host_owner_decision_record_human_record_owner_review_candidate_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_candidate_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_candidate_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_candidate_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_candidate_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_candidate_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_candidate_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_candidate_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_candidate_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_candidate_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_candidate_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_candidate_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_candidate_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_candidate_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_candidate_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_candidate_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_candidate_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_candidate_checklist_items.{item['item_id']}.not_authorization",
+ item["not_authorization"],
+ )
assert_equal(
"iwooos_projection.non_blocking_lane_ids",
iwooos_projection["non_blocking_lane_ids"],
@@ -3221,6 +3372,7 @@ def validate(root: Path) -> None:
"display_host_owner_decision_record_human_handoff_readiness_review_checklist",
"display_host_owner_decision_record_human_handoff_readiness_review_outcome_lanes",
"display_host_owner_decision_record_human_record_owner_review_candidate_packets",
+ "display_host_owner_decision_record_human_record_owner_review_candidate_checklist",
"display_evidence_refs",
"display_forbidden_actions",
]:
@@ -3347,6 +3499,13 @@ def validate(root: Path) -> None:
"collect_owner_decision_from_human_record_owner_review_candidate_packet",
"create_host_owner_decision_record_from_human_record_owner_review_candidate_packet",
"open_runtime_gate_from_human_record_owner_review_candidate_packet",
+ "treat_host_owner_decision_record_human_record_owner_review_candidate_checklist_as_approval",
+ "mark_human_record_owner_review_candidate_checklist_passed",
+ "start_human_record_owner_review_from_candidate_checklist",
+ "mark_human_record_owner_review_ready_from_candidate_checklist",
+ "collect_owner_decision_from_human_record_owner_review_candidate_checklist",
+ "create_host_owner_decision_record_from_human_record_owner_review_candidate_checklist",
+ "open_runtime_gate_from_human_record_owner_review_candidate_checklist",
"apply_runtime_blocking_control",
"switch_github_primary",
"production_deploy",