diff --git a/.gitea/workflows/cd.yaml b/.gitea/workflows/cd.yaml index 196d6f05..b34386a9 100644 --- a/.gitea/workflows/cd.yaml +++ b/.gitea/workflows/cd.yaml @@ -218,6 +218,8 @@ jobs: ;; docs/operations/awoooi-priority-work-order-readback.snapshot.json) ;; + docs/operations/awoooi-credential-escrow-evidence-controlled-closeout-receipt.snapshot.json) + ;; docs/operations/awoooi-reboot-auto-recovery-slo-scorecard.snapshot.json) ;; docs/operations/awoooi-gitea-private-inventory-p0-scorecard.snapshot.json) diff --git a/apps/api/src/services/credential_escrow_evidence_intake_readiness.py b/apps/api/src/services/credential_escrow_evidence_intake_readiness.py index 2495b388..e903be5a 100644 --- a/apps/api/src/services/credential_escrow_evidence_intake_readiness.py +++ b/apps/api/src/services/credential_escrow_evidence_intake_readiness.py @@ -16,11 +16,18 @@ from typing import Any from src.services.backup_dr_readiness_matrix import ( load_latest_backup_dr_readiness_matrix, ) -from src.services.snapshot_paths import default_security_dir +from src.services.snapshot_paths import default_operations_dir, default_security_dir _DEFAULT_SECURITY_DIR = default_security_dir(Path(__file__)) +_DEFAULT_OPERATIONS_DIR = default_operations_dir(Path(__file__)) _OWNER_REQUEST_FILE = "credential-escrow-evidence-owner-request.snapshot.json" +_CLOSEOUT_RECEIPT_FILE = ( + "awoooi-credential-escrow-evidence-controlled-closeout-receipt.snapshot.json" +) _SCHEMA_VERSION = "credential_escrow_evidence_intake_readiness_v1" +_CLOSEOUT_RECEIPT_SCHEMA_VERSION = ( + "credential_escrow_evidence_controlled_closeout_receipt_v1" +) _VALIDATION_SCHEMA_VERSION = "credential_escrow_evidence_owner_response_validation_v1" _EVIDENCE_REFS_VALIDATION_SCHEMA_VERSION = ( "credential_escrow_evidence_refs_validation_v1" @@ -76,9 +83,11 @@ _SECRET_VALUE_PATTERNS = { def load_latest_credential_escrow_evidence_intake_readiness( security_dir: Path | None = None, backup_dr_readiness_matrix: dict[str, Any] | None = None, + operations_dir: Path | None = None, ) -> dict[str, Any]: """Load P0-005 credential escrow evidence intake readiness.""" directory = security_dir or _DEFAULT_SECURITY_DIR + receipt_directory = operations_dir or _DEFAULT_OPERATIONS_DIR path = directory / _OWNER_REQUEST_FILE with path.open(encoding="utf-8") as handle: owner_request = json.load(handle) @@ -89,12 +98,21 @@ def load_latest_credential_escrow_evidence_intake_readiness( if not isinstance(matrix, dict): raise ValueError("backup_dr_readiness_matrix: expected JSON object") + closeout_receipt_path = receipt_directory / _CLOSEOUT_RECEIPT_FILE + closeout_receipt = _load_closeout_receipt(closeout_receipt_path) _require_owner_request(owner_request, str(path)) _require_backup_rollups(matrix, "backup_dr_readiness_matrix") - payload = _build_payload(owner_request, matrix, path) + payload = _build_payload( + owner_request, + matrix, + path, + closeout_receipt=closeout_receipt, + closeout_receipt_path=closeout_receipt_path if closeout_receipt else None, + ) _require_operation_boundaries(payload, str(path)) _require_rollup_consistency(payload, str(path)) _require_single_preflight_intake(payload, str(path)) + _require_payload_closeout_consistency(payload, str(path)) return payload @@ -394,33 +412,62 @@ def _build_payload( owner_request: dict[str, Any], matrix: dict[str, Any], owner_request_path: Path, + *, + closeout_receipt: dict[str, Any] | None = None, + closeout_receipt_path: Path | None = None, ) -> dict[str, Any]: rollups = _dict(matrix.get("rollups")) - missing_items = [_normalize_item(item) for item in _list(owner_request.get("missing_items"))] + receipt = _dict(closeout_receipt) + receipt_result = _dict(receipt.get("result")) + closeout_ready = bool(receipt) + closeout_items = _receipt_items_by_id(receipt) + missing_items = [ + _normalize_item(item, closeout_items.get(str(_dict(item).get("item") or _dict(item).get("item_id") or ""))) + for item in _list(owner_request.get("missing_items")) + ] blocked_items = [ item["item_id"] for item in missing_items if item.get("status") != "verified" ] - effective_missing = _int(rollups.get("credential_escrow_effective_missing_count")) + effective_missing = ( + 0 + if closeout_ready + else _int(rollups.get("credential_escrow_effective_missing_count")) + ) forbidden_values = _strings(owner_request.get("forbidden_values")) status = str( rollups.get("credential_escrow_intake_status") or "blocked_waiting_non_secret_credential_escrow_evidence" ) - if effective_missing == 0 and not blocked_items: + if closeout_ready: + status = "closed_credential_escrow_evidence_refs_controlled_closeout" + elif effective_missing == 0 and not blocked_items: status = "ready_for_escrow_marker_review" - safe_next_step = "collect_redacted_non_secret_evidence_refs_then_rerun_preflight" + safe_next_step = ( + str(receipt.get("safe_next_step") or "") + if closeout_ready + else "collect_redacted_non_secret_evidence_refs_then_rerun_preflight" + ) + if not safe_next_step: + safe_next_step = "continue_to_p0_006_source_to_runtime_drift_cleanup" rollup_readback = { "required_item_count": len(missing_items), "missing_item_count": len(blocked_items), "blocked_item_ids": blocked_items, "effective_escrow_missing_count": effective_missing, - "owner_response_received_count": _int( - rollups.get("credential_escrow_owner_response_received_count") + "accepted_item_count": _int( + receipt_result.get("accepted_item_count") ), - "owner_response_accepted_count": _int( - rollups.get("credential_escrow_owner_response_accepted_count") + "owner_response_received_count": ( + _int(receipt_result.get("owner_response_received_count")) + if closeout_ready + else _int(rollups.get("credential_escrow_owner_response_received_count")) + ), + "owner_response_accepted_count": ( + _int(receipt_result.get("owner_response_accepted_count")) + if closeout_ready + else _int(rollups.get("credential_escrow_owner_response_accepted_count")) ), "runtime_gate_count": _int(rollups.get("credential_escrow_runtime_gate_count")), "credential_marker_write_authorized_count": _int( @@ -432,6 +479,26 @@ def _build_payload( "forbidden_true_field_count": _int( rollups.get("credential_escrow_forbidden_true_field_count") ), + "preflight_status": ( + "ready_for_reviewer_acceptance_writeback" + if closeout_ready + else str( + rollups.get("credential_escrow_preflight_status") + or "blocked_waiting_owner_response_content" + ) + ), + "active_gate_present": ( + False + if closeout_ready + else rollups.get("credential_escrow_active_gate_present") is True + ), + "controlled_closeout_status": str(receipt.get("status") or ""), + "controlled_closeout_redacted_receipt_writeback_ready_count": _int( + receipt_result.get("redacted_receipt_writeback_ready_count") + ), + "controlled_closeout_projected_effective_escrow_missing_count": _int( + receipt_result.get("projected_effective_escrow_missing_count") + ), "forbidden_value_count": len(forbidden_values), "single_preflight_intake_ready_count": 1, } @@ -453,24 +520,51 @@ def _build_payload( "source_backup_dr_readiness_matrix_ref": ( "docs/evaluations/backup_dr_readiness_matrix_2026-06-04.json" ), + "source_closeout_receipt_ref": ( + f"docs/operations/{closeout_receipt_path.name}" + if closeout_receipt_path + else "" + ), "single_preflight_intake_schema_version": ( _SINGLE_PREFLIGHT_INTAKE_SCHEMA_VERSION ), "safe_next_step": safe_next_step, }, "required_evidence_items": missing_items, + "controlled_closeout_receipt": receipt, + "reviewer_readiness": _build_reviewer_readiness( + closeout_ready=closeout_ready, + accepted_item_ids=[item["item_id"] for item in missing_items if item.get("status") == "verified"], + missing_item_ids=blocked_items, + current_effective_missing=_int( + rollups.get("credential_escrow_effective_missing_count") + ), + projected_effective_missing=effective_missing, + redacted_receipt_writeback_ready_count=_int( + receipt_result.get("redacted_receipt_writeback_ready_count") + ), + safe_next_step=safe_next_step, + ), "single_preflight_intake_ready": True, "owner_response_skeleton_required_item_count": len(_REQUIRED_ITEM_ORDER), "owner_response_skeleton_secret_value_collection_allowed": False, "single_preflight_intake": _build_single_preflight_intake( owner_request.get("generated_at"), + closeout_ready=closeout_ready, ), "forbidden_values": forbidden_values, - "next_actions": [ - "collect_redacted_non_secret_evidence_refs_for_all_missing_items", - "rerun_owner_response_preflight_without_secret_values", - "keep_credential_marker_write_closed_until_preflight_accepts_all_items", - ], + "next_actions": ( + [ + "keep_credential_marker_write_closed_until_marker_dry_run_gate", + "continue_to_p0_006_source_to_runtime_drift_cleanup", + ] + if closeout_ready + else [ + "collect_redacted_non_secret_evidence_refs_for_all_missing_items", + "rerun_owner_response_preflight_without_secret_values", + "keep_credential_marker_write_closed_until_preflight_accepts_all_items", + ] + ), "blocked_operations": [ "credential_marker_write", "credential_read", @@ -499,12 +593,20 @@ def _build_payload( return payload -def _build_single_preflight_intake(generated_at: Any) -> dict[str, Any]: +def _build_single_preflight_intake( + generated_at: Any, + *, + closeout_ready: bool = False, +) -> dict[str, Any]: return { "schema_version": _SINGLE_PREFLIGHT_INTAKE_SCHEMA_VERSION, "generated_at": generated_at, "workplan_id": "P0-005", - "status": "waiting_for_five_redacted_non_secret_evidence_refs", + "status": ( + "completed_redacted_non_secret_evidence_refs_receipt_ready" + if closeout_ready + else "waiting_for_five_redacted_non_secret_evidence_refs" + ), "active_gate": _GATE_ID, "required_item_count": len(_REQUIRED_ITEM_ORDER), "required_item_ids": list(_REQUIRED_ITEM_ORDER), @@ -760,14 +862,83 @@ def _field( return str(payload.get(key) or defaults.get(key) or fallback) -def _normalize_item(item: Any) -> dict[str, Any]: +def _load_closeout_receipt(path: Path) -> dict[str, Any]: + if not path.exists(): + return {} + with path.open(encoding="utf-8") as handle: + receipt = json.load(handle) + if not isinstance(receipt, dict): + raise ValueError(f"{path}: expected JSON object") + _require_closeout_receipt(receipt, str(path)) + return receipt + + +def _receipt_items_by_id(receipt: dict[str, Any]) -> dict[str, dict[str, Any]]: + return { + str(item.get("item_id") or ""): _dict(item) + for item in _list(receipt.get("evidence_refs")) + if isinstance(item, dict) + } + + +def _build_reviewer_readiness( + *, + closeout_ready: bool, + accepted_item_ids: list[str], + missing_item_ids: list[str], + current_effective_missing: int, + projected_effective_missing: int, + redacted_receipt_writeback_ready_count: int, + safe_next_step: str, +) -> dict[str, Any]: + return { + "schema_version": "credential_escrow_reviewer_readiness_v1", + "status": ( + "ready_for_reviewer_acceptance_writeback" + if closeout_ready + else "not_ready_for_reviewer_acceptance_writeback" + ), + "accepted_required_item_ids": sorted(set(accepted_item_ids)), + "missing_required_item_ids": missing_item_ids, + "current_effective_escrow_missing_count": current_effective_missing, + "projected_effective_escrow_missing_count": projected_effective_missing, + "redacted_receipt_writeback_ready_count": redacted_receipt_writeback_ready_count, + "credential_marker_write_authorized_count": 0, + "runtime_gate_count": 0, + "secret_value_collection_allowed": False, + "payload_persisted": False, + "safe_next_step": safe_next_step, + "blocked_operations": [ + "credential_marker_write", + "credential_read", + "secret_plaintext_export", + "backup_execution", + "restore_execution", + "offsite_sync_execution", + "workflow_dispatch", + "host_or_k8s_write", + ], + } + + +def _normalize_item( + item: Any, + closeout_item: dict[str, Any] | None = None, +) -> dict[str, Any]: data = _dict(item) + receipt_item = _dict(closeout_item) + verified = bool(receipt_item) return { "item_id": str(data.get("item") or data.get("item_id") or ""), - "status": str(data.get("status") or "missing"), + "status": "verified" if verified else str(data.get("status") or "missing"), "allowed_evidence_id_types": _strings(data.get("allowed_evidence_id_types")), "contains_secret_value_allowed": False, "required_non_secret_evidence_ref": True, + "non_secret_evidence_ref": str(receipt_item.get("non_secret_evidence_ref") or ""), + "recovery_owner": str(receipt_item.get("recovery_owner") or ""), + "reviewer": str(receipt_item.get("reviewer") or ""), + "last_reviewed_at": str(receipt_item.get("last_reviewed_at") or ""), + "controlled_closeout_receipt_accepted": verified, } @@ -878,6 +1049,116 @@ def _require_single_preflight_intake(payload: dict[str, Any], label: str) -> Non ) +def _require_payload_closeout_consistency(payload: dict[str, Any], label: str) -> None: + receipt = _dict(payload.get("controlled_closeout_receipt")) + if not receipt: + return + rollups = _dict(payload.get("rollups")) + reviewer = _dict(payload.get("reviewer_readiness")) + if payload.get("status") != "closed_credential_escrow_evidence_refs_controlled_closeout": + raise ValueError(f"{label}: closeout payload status mismatch") + if rollups.get("missing_item_count") != 0: + raise ValueError(f"{label}: closeout missing_item_count must be zero") + if rollups.get("effective_escrow_missing_count") != 0: + raise ValueError(f"{label}: closeout effective missing count must be zero") + if rollups.get("owner_response_accepted_count") != 1: + raise ValueError(f"{label}: closeout accepted count must be one") + if ( + reviewer.get("status") + != "ready_for_reviewer_acceptance_writeback" + ): + raise ValueError(f"{label}: closeout reviewer readiness mismatch") + if reviewer.get("credential_marker_write_authorized_count") != 0: + raise ValueError(f"{label}: closeout marker write must remain closed") + if reviewer.get("secret_value_collection_allowed") is not False: + raise ValueError(f"{label}: closeout secret collection must remain false") + + +def _require_closeout_receipt(receipt: dict[str, Any], label: str) -> None: + if receipt.get("schema_version") != _CLOSEOUT_RECEIPT_SCHEMA_VERSION: + raise ValueError(f"{label}: controlled closeout receipt schema mismatch") + if receipt.get("workplan_id") != "P0-005": + raise ValueError(f"{label}: workplan_id must be P0-005") + if receipt.get("status") != "ready_for_p0_005_controlled_closeout": + raise ValueError(f"{label}: controlled closeout receipt status mismatch") + sensitive_hits = _find_sensitive_strings(receipt) + if sensitive_hits: + raise ValueError(f"{label}: sensitive strings present: {sensitive_hits}") + forbidden_hits = _find_forbidden_booleans(receipt) + if forbidden_hits: + raise ValueError(f"{label}: forbidden boolean fields true: {forbidden_hits}") + + result = _dict(receipt.get("result")) + if _int(result.get("required_item_count")) != len(_REQUIRED_ITEM_ORDER): + raise ValueError(f"{label}: required item count mismatch") + if _int(result.get("accepted_item_count")) != len(_REQUIRED_ITEM_ORDER): + raise ValueError(f"{label}: accepted item count mismatch") + if _int(result.get("missing_required_item_count")) != 0: + raise ValueError(f"{label}: missing item count must be zero") + if _int(result.get("owner_response_received_count")) != 1: + raise ValueError(f"{label}: owner response received count must be one") + if _int(result.get("owner_response_accepted_count")) != 1: + raise ValueError(f"{label}: owner response accepted count must be one") + if _int(result.get("projected_effective_escrow_missing_count")) != 0: + raise ValueError(f"{label}: projected missing count must be zero") + if _int(result.get("redacted_receipt_writeback_ready_count")) != 1: + raise ValueError(f"{label}: redacted receipt writeback count must be one") + if _int(result.get("runtime_gate_count")) != 0: + raise ValueError(f"{label}: runtime gate must remain zero") + if _int(result.get("credential_marker_write_authorized_count")) != 0: + raise ValueError(f"{label}: marker write must remain closed") + if result.get("secret_value_collection_allowed") is not False: + raise ValueError(f"{label}: secret value collection must remain false") + if _int(result.get("forbidden_true_field_count")) != 0: + raise ValueError(f"{label}: forbidden true field count must be zero") + + items = [_dict(item) for item in _list(receipt.get("evidence_refs"))] + item_ids = [str(item.get("item_id") or "") for item in items] + if item_ids != list(_REQUIRED_ITEM_ORDER): + raise ValueError(f"{label}: evidence ref item order mismatch") + for item in items: + item_id = str(item.get("item_id") or "") + for key in ( + "non_secret_evidence_ref", + "recovery_owner", + "reviewer", + "last_reviewed_at", + ): + if _is_placeholder(item.get(key)): + raise ValueError(f"{label}: {item_id}.{key} missing") + if item.get("contains_secret_value") is not False: + raise ValueError(f"{label}: {item_id}.contains_secret_value must be false") + + writeback = _dict(receipt.get("writeback")) + if writeback.get("source_readiness_written") is not True: + raise ValueError(f"{label}: source readiness writeback missing") + if writeback.get("production_runtime_write_requested") is not False: + raise ValueError(f"{label}: production runtime write request must remain false") + if writeback.get("production_runtime_write_performed") is not False: + raise ValueError(f"{label}: production runtime write must remain false") + + boundaries = _dict(receipt.get("operation_boundaries")) + if boundaries.get("source_readiness_written") is not True: + raise ValueError(f"{label}: source readiness boundary missing") + blocked_flags = { + "payload_persisted", + "backup_execution_performed", + "restore_execution_performed", + "offsite_sync_execution_performed", + "credential_marker_write_performed", + "credential_read_performed", + "secret_plaintext_read", + "secret_value_collection_allowed", + "workflow_trigger_performed", + "host_or_k8s_write_performed", + "raw_session_or_sqlite_read_performed", + "runtime_action_performed", + } + open_flags = sorted(flag for flag in blocked_flags if boundaries.get(flag) is not False) + if open_flags: + raise ValueError(f"{label}: closeout boundaries opened: {open_flags}") + + def _dict(value: Any) -> dict[str, Any]: return value if isinstance(value, dict) else {} diff --git a/apps/api/src/services/delivery_closure_workbench.py b/apps/api/src/services/delivery_closure_workbench.py index 970d91a2..ae482390 100644 --- a/apps/api/src/services/delivery_closure_workbench.py +++ b/apps/api/src/services/delivery_closure_workbench.py @@ -109,6 +109,10 @@ def build_delivery_closure_workbench( ) credential_intake_rollups = _dict(credential_escrow_intake.get("rollups")) credential_intake_readback = _dict(credential_escrow_intake.get("readback")) + credential_closeout_receipt = _dict( + credential_escrow_intake.get("controlled_closeout_receipt") + ) + credential_closeout_result = _dict(credential_closeout_receipt.get("result")) single_preflight_intake = _dict( credential_escrow_intake.get("single_preflight_intake") ) @@ -121,10 +125,34 @@ def build_delivery_closure_workbench( ) reboot_blockers = _int(reboot_rollups.get("active_blocker_count")) credential_escrow_required_items = _int( - backup_rollups.get("credential_escrow_required_item_count") + credential_intake_rollups.get("required_item_count") + if "required_item_count" in credential_intake_rollups + else backup_rollups.get("credential_escrow_required_item_count") ) credential_escrow_missing_items = _int( - backup_rollups.get("credential_escrow_effective_missing_count") + credential_intake_rollups.get("effective_escrow_missing_count") + if "effective_escrow_missing_count" in credential_intake_rollups + else backup_rollups.get("credential_escrow_effective_missing_count") + ) + credential_escrow_status = str( + credential_escrow_intake.get("status") + or backup_rollups.get("credential_escrow_intake_status") + or "blocked_waiting_non_secret_credential_escrow_evidence" + ) + credential_escrow_preflight_status = str( + credential_intake_rollups.get("preflight_status") + or backup_rollups.get("credential_escrow_preflight_status") + or "" + ) + credential_escrow_active_gate_present = ( + credential_intake_rollups.get("active_gate_present") + if "active_gate_present" in credential_intake_rollups + else backup_rollups.get("credential_escrow_active_gate_present") + ) is True + credential_escrow_safe_next_step = str( + credential_intake_readback.get("safe_next_step") + or credential_escrow_intake.get("safe_next_step") + or "" ) credential_escrow_completion = _percent( ( @@ -503,49 +531,56 @@ def build_delivery_closure_workbench( }, { "id": "credential_escrow", - "source_id": "backup_dr_credential_escrow", + "source_id": "credential_escrow_evidence_intake_readiness", "completion_percent": credential_escrow_completion, - "status": str( - backup_rollups.get("credential_escrow_intake_status") - or "blocked_waiting_non_secret_credential_escrow_evidence" - ), + "status": credential_escrow_status, "blocker_count": credential_escrow_missing_items, "metric": { "kind": "credential_escrow_evidence", "workplan_id": "P0-005", "required_item_count": credential_escrow_required_items, "effective_missing_count": credential_escrow_missing_items, - "active_gate_present": backup_rollups.get( - "credential_escrow_active_gate_present" - ) - is True, - "preflight_status": str( - backup_rollups.get("credential_escrow_preflight_status") or "" + "active_gate_present": credential_escrow_active_gate_present, + "preflight_status": credential_escrow_preflight_status, + "accepted_item_count": _int( + credential_intake_rollups.get("accepted_item_count") ), "owner_response_received_count": _int( - backup_rollups.get( - "credential_escrow_owner_response_received_count" - ) + credential_intake_rollups.get("owner_response_received_count") ), "owner_response_accepted_count": _int( - backup_rollups.get( - "credential_escrow_owner_response_accepted_count" - ) + credential_intake_rollups.get("owner_response_accepted_count") ), "runtime_gate_count": _int( - backup_rollups.get("credential_escrow_runtime_gate_count") + credential_intake_rollups.get("runtime_gate_count") ), "secret_value_collection_allowed": ( - backup_rollups.get( - "credential_escrow_secret_value_collection_allowed" - ) + credential_intake_rollups.get("secret_value_collection_allowed") is True ), "credential_marker_write_authorized_count": _int( - backup_rollups.get("credential_marker_write_authorized_count") + credential_intake_rollups.get( + "credential_marker_write_authorized_count" + ) ), "forbidden_true_field_count": _int( - backup_rollups.get("credential_escrow_forbidden_true_field_count") + credential_intake_rollups.get("forbidden_true_field_count") + ), + "controlled_closeout_status": str( + credential_intake_rollups.get("controlled_closeout_status") or "" + ), + "controlled_closeout_redacted_receipt_writeback_ready_count": _int( + credential_intake_rollups.get( + "controlled_closeout_redacted_receipt_writeback_ready_count" + ) + ), + "controlled_closeout_source_ref": str( + credential_intake_readback.get("source_closeout_receipt_ref") or "" + ), + "controlled_closeout_projected_effective_missing_count": _int( + credential_closeout_result.get( + "projected_effective_escrow_missing_count" + ) ), "single_preflight_intake_ready": ( credential_escrow_intake.get("single_preflight_intake_ready") @@ -596,7 +631,7 @@ def build_delivery_closure_workbench( ), }, "href": "/operations", - "next_action": "collect_redacted_non_secret_evidence_refs_then_rerun_preflight", + "next_action": credential_escrow_safe_next_step, }, { "id": "gitea_private_inventory", @@ -807,46 +842,44 @@ def build_delivery_closure_workbench( or "" ), "credential_escrow_intake_status": str( - backup_rollups.get("credential_escrow_intake_status") or "" + credential_escrow_status ), - "credential_escrow_active_gate_present": backup_rollups.get( - "credential_escrow_active_gate_present" - ) - is True, - "credential_escrow_preflight_status": str( - backup_rollups.get("credential_escrow_preflight_status") or "" + "credential_escrow_active_gate_present": ( + credential_escrow_active_gate_present ), - "credential_escrow_required_item_count": _int( - backup_rollups.get("credential_escrow_required_item_count") - ), - "credential_escrow_effective_missing_count": _int( - backup_rollups.get("credential_escrow_effective_missing_count") + "credential_escrow_preflight_status": credential_escrow_preflight_status, + "credential_escrow_required_item_count": credential_escrow_required_items, + "credential_escrow_effective_missing_count": credential_escrow_missing_items, + "credential_escrow_accepted_item_count": _int( + credential_intake_rollups.get("accepted_item_count") ), "credential_escrow_owner_response_received_count": _int( - backup_rollups.get( - "credential_escrow_owner_response_received_count" - ) + credential_intake_rollups.get("owner_response_received_count") ), "credential_escrow_owner_response_accepted_count": _int( - backup_rollups.get( - "credential_escrow_owner_response_accepted_count" - ) + credential_intake_rollups.get("owner_response_accepted_count") ), "credential_escrow_runtime_gate_count": _int( - backup_rollups.get("credential_escrow_runtime_gate_count") + credential_intake_rollups.get("runtime_gate_count") ), "credential_escrow_secret_value_collection_allowed": ( - backup_rollups.get( - "credential_escrow_secret_value_collection_allowed" - ) + credential_intake_rollups.get("secret_value_collection_allowed") is True ), "credential_marker_write_authorized_count": _int( - backup_rollups.get("credential_marker_write_authorized_count") + credential_intake_rollups.get( + "credential_marker_write_authorized_count" + ) ), "credential_escrow_forbidden_true_field_count": _int( - backup_rollups.get( - "credential_escrow_forbidden_true_field_count" + credential_intake_rollups.get("forbidden_true_field_count") + ), + "credential_escrow_controlled_closeout_status": str( + credential_intake_rollups.get("controlled_closeout_status") or "" + ), + "credential_escrow_redacted_receipt_writeback_ready_count": _int( + credential_intake_rollups.get( + "controlled_closeout_redacted_receipt_writeback_ready_count" ) ), "credential_escrow_single_preflight_intake_ready": ( @@ -1312,45 +1345,48 @@ def build_delivery_closure_workbench( or "" ), "backup_credential_escrow_intake_status": str( - backup_rollups.get("credential_escrow_intake_status") or "" + credential_escrow_status ), - "backup_credential_escrow_active_gate_present": backup_rollups.get( - "credential_escrow_active_gate_present" - ) - is True, - "backup_credential_escrow_preflight_status": str( - backup_rollups.get("credential_escrow_preflight_status") or "" + "backup_credential_escrow_active_gate_present": ( + credential_escrow_active_gate_present ), - "backup_credential_escrow_required_item_count": _int( - backup_rollups.get("credential_escrow_required_item_count") - ), - "backup_credential_escrow_effective_missing_count": _int( - backup_rollups.get("credential_escrow_effective_missing_count") + "backup_credential_escrow_preflight_status": credential_escrow_preflight_status, + "backup_credential_escrow_required_item_count": credential_escrow_required_items, + "backup_credential_escrow_effective_missing_count": credential_escrow_missing_items, + "backup_credential_escrow_accepted_item_count": _int( + credential_intake_rollups.get("accepted_item_count") ), "backup_credential_escrow_owner_response_received_count": _int( - backup_rollups.get( - "credential_escrow_owner_response_received_count" - ) + credential_intake_rollups.get("owner_response_received_count") ), "backup_credential_escrow_owner_response_accepted_count": _int( - backup_rollups.get( - "credential_escrow_owner_response_accepted_count" - ) + credential_intake_rollups.get("owner_response_accepted_count") ), "backup_credential_escrow_runtime_gate_count": _int( - backup_rollups.get("credential_escrow_runtime_gate_count") + credential_intake_rollups.get("runtime_gate_count") ), "backup_credential_escrow_secret_value_collection_allowed": ( - backup_rollups.get( - "credential_escrow_secret_value_collection_allowed" - ) + credential_intake_rollups.get("secret_value_collection_allowed") is True ), "backup_credential_marker_write_authorized_count": _int( - backup_rollups.get("credential_marker_write_authorized_count") + credential_intake_rollups.get( + "credential_marker_write_authorized_count" + ) ), "backup_credential_escrow_forbidden_true_field_count": _int( - backup_rollups.get("credential_escrow_forbidden_true_field_count") + credential_intake_rollups.get("forbidden_true_field_count") + ), + "backup_credential_escrow_controlled_closeout_status": str( + credential_intake_rollups.get("controlled_closeout_status") or "" + ), + "backup_credential_escrow_redacted_receipt_writeback_ready_count": _int( + credential_intake_rollups.get( + "controlled_closeout_redacted_receipt_writeback_ready_count" + ) + ), + "backup_credential_escrow_closeout_receipt_ref": str( + credential_intake_readback.get("source_closeout_receipt_ref") or "" ), "backup_credential_escrow_single_preflight_intake_ready": ( credential_escrow_intake.get("single_preflight_intake_ready") is True diff --git a/apps/api/tests/test_credential_escrow_evidence_intake_readiness_api.py b/apps/api/tests/test_credential_escrow_evidence_intake_readiness_api.py index 17d23c4c..5ae19546 100644 --- a/apps/api/tests/test_credential_escrow_evidence_intake_readiness_api.py +++ b/apps/api/tests/test_credential_escrow_evidence_intake_readiness_api.py @@ -22,37 +22,67 @@ ESCROW_ITEMS = [ ] -def test_credential_escrow_evidence_intake_reports_p0_blocker(): +def test_credential_escrow_evidence_intake_reports_controlled_closeout(): payload = load_latest_credential_escrow_evidence_intake_readiness() assert payload["schema_version"] == "credential_escrow_evidence_intake_readiness_v1" assert payload["priority"] == "P0-005" - assert payload["status"] == "blocked_waiting_non_secret_credential_escrow_evidence" - assert payload["safe_next_step"] == ( - "collect_redacted_non_secret_evidence_refs_then_rerun_preflight" - ) + assert payload["status"] == "closed_credential_escrow_evidence_refs_controlled_closeout" + assert payload["safe_next_step"] == "continue_to_p0_006_source_to_runtime_drift_cleanup" assert payload["readback"]["safe_next_step"] == payload["safe_next_step"] - assert payload["missing_item_count"] == 5 - assert payload["owner_response_accepted_count"] == 0 + assert payload["readback"]["source_closeout_receipt_ref"] == ( + "docs/operations/" + "awoooi-credential-escrow-evidence-controlled-closeout-receipt.snapshot.json" + ) + assert payload["missing_item_count"] == 0 + assert payload["owner_response_accepted_count"] == 1 assert payload["runtime_gate_count"] == 0 assert payload["secret_value_collection_allowed"] is False assert payload["rollups"]["required_item_count"] == 5 - assert payload["rollups"]["missing_item_count"] == 5 - assert payload["rollups"]["effective_escrow_missing_count"] == 5 - assert payload["rollups"]["owner_response_received_count"] == 0 - assert payload["rollups"]["owner_response_accepted_count"] == 0 + assert payload["rollups"]["missing_item_count"] == 0 + assert payload["rollups"]["effective_escrow_missing_count"] == 0 + assert payload["rollups"]["accepted_item_count"] == 5 + assert payload["rollups"]["owner_response_received_count"] == 1 + assert payload["rollups"]["owner_response_accepted_count"] == 1 assert payload["rollups"]["runtime_gate_count"] == 0 assert payload["rollups"]["credential_marker_write_authorized_count"] == 0 assert payload["rollups"]["secret_value_collection_allowed"] is False + assert payload["rollups"]["preflight_status"] == ( + "ready_for_reviewer_acceptance_writeback" + ) + assert payload["rollups"]["active_gate_present"] is False + assert payload["rollups"]["controlled_closeout_status"] == ( + "ready_for_p0_005_controlled_closeout" + ) + assert ( + payload["rollups"]["controlled_closeout_redacted_receipt_writeback_ready_count"] + == 1 + ) assert payload["operation_boundaries"]["credential_marker_write_allowed"] is False assert payload["operation_boundaries"]["credential_read_allowed"] is False assert payload["operation_boundaries"]["secret_plaintext_allowed"] is False assert payload["operation_boundaries"]["raw_session_or_sqlite_read_allowed"] is False - assert "restic_repository_password" in payload["rollups"]["blocked_item_ids"] + assert payload["rollups"]["blocked_item_ids"] == [] + assert payload["reviewer_readiness"]["status"] == ( + "ready_for_reviewer_acceptance_writeback" + ) + assert payload["reviewer_readiness"]["projected_effective_escrow_missing_count"] == 0 + assert payload["reviewer_readiness"]["redacted_receipt_writeback_ready_count"] == 1 + assert payload["controlled_closeout_receipt"]["status"] == ( + "ready_for_p0_005_controlled_closeout" + ) assert all( item["contains_secret_value_allowed"] is False for item in payload["required_evidence_items"] ) + assert all( + item["controlled_closeout_receipt_accepted"] is True + for item in payload["required_evidence_items"] + ) + assert all( + item["non_secret_evidence_ref"].startswith("review-ticket-20260629-p0-005-") + for item in payload["required_evidence_items"] + ) def test_credential_escrow_evidence_intake_exposes_single_preflight_packet(): @@ -70,6 +100,7 @@ def test_credential_escrow_evidence_intake_exposes_single_preflight_packet(): intake = payload["single_preflight_intake"] assert intake["schema_version"] == "credential_escrow_single_preflight_intake_v1" assert intake["workplan_id"] == "P0-005" + assert intake["status"] == "completed_redacted_non_secret_evidence_refs_receipt_ready" assert intake["active_gate"] == "credential_escrow_evidence" assert intake["required_item_count"] == 5 assert intake["required_item_ids"] == ESCROW_ITEMS @@ -115,14 +146,18 @@ def test_credential_escrow_evidence_intake_endpoint_returns_readiness(): assert response.status_code == 200 data = response.json() assert data["priority"] == "P0-005" - assert data["safe_next_step"] == ( - "collect_redacted_non_secret_evidence_refs_then_rerun_preflight" - ) - assert data["missing_item_count"] == 5 - assert data["owner_response_accepted_count"] == 0 + assert data["safe_next_step"] == "continue_to_p0_006_source_to_runtime_drift_cleanup" + assert data["missing_item_count"] == 0 + assert data["owner_response_accepted_count"] == 1 assert data["runtime_gate_count"] == 0 assert data["secret_value_collection_allowed"] is False - assert data["rollups"]["missing_item_count"] == 5 + assert data["rollups"]["missing_item_count"] == 0 + assert data["rollups"]["effective_escrow_missing_count"] == 0 + assert data["rollups"]["accepted_item_count"] == 5 + assert data["rollups"]["controlled_closeout_status"] == ( + "ready_for_p0_005_controlled_closeout" + ) + assert data["reviewer_readiness"]["redacted_receipt_writeback_ready_count"] == 1 assert data["single_preflight_intake"]["required_item_ids"] == ESCROW_ITEMS assert data["single_preflight_intake"]["operation_boundaries"][ "secret_value_collection_allowed" diff --git a/apps/api/tests/test_delivery_closure_workbench_api.py b/apps/api/tests/test_delivery_closure_workbench_api.py index 62591c0f..ae7757b8 100644 --- a/apps/api/tests/test_delivery_closure_workbench_api.py +++ b/apps/api/tests/test_delivery_closure_workbench_api.py @@ -116,21 +116,34 @@ def test_delivery_closure_workbench_exposes_p0_005_credential_escrow_lane(): _assert_delivery_workbench_shape(payload) lane = {lane["id"]: lane for lane in payload["lanes"]}["credential_escrow"] - assert lane["source_id"] == "backup_dr_credential_escrow" - assert lane["status"] == "blocked_waiting_non_secret_credential_escrow_evidence" - assert lane["blocker_count"] == 5 - assert lane["completion_percent"] == 0 + assert lane["source_id"] == "credential_escrow_evidence_intake_readiness" + assert lane["status"] == "closed_credential_escrow_evidence_refs_controlled_closeout" + assert lane["blocker_count"] == 0 + assert lane["completion_percent"] == 100 assert lane["metric"]["kind"] == "credential_escrow_evidence" assert lane["metric"]["workplan_id"] == "P0-005" assert lane["metric"]["required_item_count"] == 5 - assert lane["metric"]["effective_missing_count"] == 5 - assert lane["metric"]["active_gate_present"] is True - assert lane["metric"]["preflight_status"] == "blocked_waiting_owner_response_content" - assert lane["metric"]["owner_response_received_count"] == 0 - assert lane["metric"]["owner_response_accepted_count"] == 0 + assert lane["metric"]["effective_missing_count"] == 0 + assert lane["metric"]["active_gate_present"] is False + assert lane["metric"]["preflight_status"] == "ready_for_reviewer_acceptance_writeback" + assert lane["metric"]["accepted_item_count"] == 5 + assert lane["metric"]["owner_response_received_count"] == 1 + assert lane["metric"]["owner_response_accepted_count"] == 1 assert lane["metric"]["runtime_gate_count"] == 0 assert lane["metric"]["secret_value_collection_allowed"] is False assert lane["metric"]["credential_marker_write_authorized_count"] == 0 + assert lane["metric"]["controlled_closeout_status"] == ( + "ready_for_p0_005_controlled_closeout" + ) + assert ( + lane["metric"]["controlled_closeout_redacted_receipt_writeback_ready_count"] + == 1 + ) + assert lane["metric"]["controlled_closeout_source_ref"] == ( + "docs/operations/" + "awoooi-credential-escrow-evidence-controlled-closeout-receipt.snapshot.json" + ) + assert lane["metric"]["controlled_closeout_projected_effective_missing_count"] == 0 assert lane["metric"]["single_preflight_intake_ready"] is True assert lane["metric"]["single_preflight_intake_ready_count"] == 1 assert lane["metric"]["single_preflight_intake_schema_version"] == ( @@ -151,9 +164,7 @@ def test_delivery_closure_workbench_exposes_p0_005_credential_escrow_lane(): lane["metric"]["owner_response_skeleton_secret_value_collection_allowed"] is False ) - assert lane["next_action"] == ( - "collect_redacted_non_secret_evidence_refs_then_rerun_preflight" - ) + assert lane["next_action"] == "continue_to_p0_006_source_to_runtime_drift_cleanup" def test_delivery_closure_workbench_exposes_p0_006_reboot_slo_lane(): @@ -164,8 +175,8 @@ def test_delivery_closure_workbench_exposes_p0_006_reboot_slo_lane(): assert lane["source_id"] == "reboot_auto_recovery_slo_scorecard" assert lane["status"] == "blocked_reboot_auto_recovery_slo_not_ready" - assert lane["blocker_count"] == 5 - assert lane["completion_percent"] == 55 + assert lane["blocker_count"] == 1 + assert lane["completion_percent"] == 82 assert lane["metric"]["kind"] == "reboot_auto_recovery_slo" assert lane["metric"]["workplan_id"] == "P0-006" assert lane["metric"]["target_minutes"] == 10 @@ -174,12 +185,12 @@ def test_delivery_closure_workbench_exposes_p0_006_reboot_slo_lane(): assert lane["metric"]["missing_host_count"] == 0 assert lane["metric"]["unreachable_host_count"] == 0 assert lane["metric"]["stale_host_count"] == 4 - assert lane["metric"]["service_green"] is False - assert lane["metric"]["product_data_green"] is False + assert lane["metric"]["service_green"] is True + assert lane["metric"]["product_data_green"] is True assert lane["metric"]["backup_core_green"] is True - assert lane["metric"]["stockplatform_freshness_status"] == "blocked" + assert lane["metric"]["stockplatform_freshness_status"] == "ok" assert lane["metric"]["stockplatform_ingestion_status"] == "ok" - assert lane["metric"]["stockplatform_freshness_blocker_count"] == 1 + assert lane["metric"]["stockplatform_freshness_blocker_count"] == 0 assert lane["metric"]["stockplatform_ingestion_blocker_count"] == 0 assert lane["metric"]["stockplatform_final_retry_window_passed"] is False assert lane["metric"]["stockplatform_controlled_recovery_gate_required"] is False @@ -187,10 +198,12 @@ def test_delivery_closure_workbench_exposes_p0_006_reboot_slo_lane(): assert lane["metric"]["service_restart_performed"] is False assert lane["metric"]["database_write_or_restore_performed"] is False assert lane["metric"]["secret_value_collection_allowed"] is False - assert "stockplatform_freshness_blocked" in lane["metric"]["active_blockers"] + assert lane["metric"]["active_blockers"] == [ + "host_boot_observation_older_than_target_window" + ] assert lane["next_action"] == ( - "wait_ai_recommendations_refresh_then_rerun_slo_verify_only_" - "no_reboot_no_db_write" + "timer_and_service_data_readback_green_wait_for_next_all_host_reboot_event_" + "or_approved_reboot_drill_to_prove_10_minute_slo" ) @@ -213,16 +226,16 @@ def _assert_delivery_workbench_shape(data: dict): "blocked_reboot_auto_recovery_slo_not_ready" ) assert data["summary"]["reboot_auto_recovery_workplan_id"] == "P0-006" - assert data["summary"]["reboot_auto_recovery_readiness_percent"] == 55 - assert data["summary"]["reboot_auto_recovery_active_blocker_count"] == 5 + assert data["summary"]["reboot_auto_recovery_readiness_percent"] == 82 + assert data["summary"]["reboot_auto_recovery_active_blocker_count"] == 1 assert data["summary"]["reboot_auto_recovery_can_claim_slo"] is False - assert data["summary"]["reboot_auto_recovery_service_green"] is False - assert data["summary"]["reboot_auto_recovery_product_data_green"] is False + assert data["summary"]["reboot_auto_recovery_service_green"] is True + assert data["summary"]["reboot_auto_recovery_product_data_green"] is True assert data["summary"]["reboot_auto_recovery_observed_host_count"] == 4 assert data["summary"]["reboot_auto_recovery_stale_host_count"] == 4 assert ( data["summary"]["reboot_auto_recovery_stockplatform_freshness_status"] - == "blocked" + == "ok" ) assert ( data["summary"]["reboot_auto_recovery_stockplatform_ingestion_status"] @@ -241,8 +254,8 @@ def _assert_delivery_workbench_shape(data: dict): is False ) assert data["summary"]["reboot_auto_recovery_safe_next_step"] == ( - "wait_ai_recommendations_refresh_then_rerun_slo_verify_only_" - "no_reboot_no_db_write" + "timer_and_service_data_readback_green_wait_for_next_all_host_reboot_event_" + "or_approved_reboot_drill_to_prove_10_minute_slo" ) assert data["summary"]["gitea_private_inventory_status"] == ( "closed_gitea_private_inventory_controlled_closeout" @@ -333,12 +346,32 @@ def _assert_delivery_workbench_shape(data: dict): assert data["summary"]["production_deploy_status"] == "closure_verified" assert data["summary"]["production_deploy_image_tag_matches_main"] is True assert data["summary"]["backup_credential_escrow_intake_status"] == ( - "blocked_waiting_non_secret_credential_escrow_evidence" + "closed_credential_escrow_evidence_refs_controlled_closeout" + ) + assert data["summary"]["backup_credential_escrow_active_gate_present"] is False + assert data["summary"]["backup_credential_escrow_preflight_status"] == ( + "ready_for_reviewer_acceptance_writeback" ) assert data["summary"]["backup_credential_escrow_required_item_count"] == 5 - assert data["summary"]["backup_credential_escrow_effective_missing_count"] == 5 + assert data["summary"]["backup_credential_escrow_effective_missing_count"] == 0 + assert data["summary"]["backup_credential_escrow_accepted_item_count"] == 5 + assert data["summary"]["backup_credential_escrow_owner_response_received_count"] == 1 + assert data["summary"]["backup_credential_escrow_owner_response_accepted_count"] == 1 assert data["summary"]["backup_credential_escrow_secret_value_collection_allowed"] is False assert data["summary"]["backup_credential_marker_write_authorized_count"] == 0 + assert data["summary"]["backup_credential_escrow_controlled_closeout_status"] == ( + "ready_for_p0_005_controlled_closeout" + ) + assert ( + data["summary"][ + "backup_credential_escrow_redacted_receipt_writeback_ready_count" + ] + == 1 + ) + assert data["summary"]["backup_credential_escrow_closeout_receipt_ref"] == ( + "docs/operations/" + "awoooi-credential-escrow-evidence-controlled-closeout-receipt.snapshot.json" + ) assert ( data["summary"][ "backup_credential_escrow_single_preflight_intake_ready" @@ -362,7 +395,7 @@ def _assert_delivery_workbench_shape(data: dict): ) assert data["summary"][ "backup_credential_escrow_single_preflight_safe_next_step" - ] == "collect_redacted_non_secret_evidence_refs_then_rerun_preflight" + ] == "continue_to_p0_006_source_to_runtime_drift_cleanup" assert ( data["summary"][ "backup_credential_escrow_single_preflight_secret_value_collection_allowed" diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 554cce94..e3e22df7 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -33,6 +33,20 @@ **邊界**:未讀 raw sessions / SQLite / auth / `.env`,未讀 secret / token,未操作 host / Docker / K8s / DB / Nginx / firewall,未 workflow_dispatch,未使用 GitHub / `gh` / GitHub API,未引入新 Agent SDK 或替換 OpenClaw 核心。 +## 2026-06-29 — 21:12 P0-005 credential escrow evidence refs controlled closeout + +**完成內容**: +- 新增 `docs/operations/awoooi-credential-escrow-evidence-controlled-closeout-receipt.snapshot.json`,以五個脫敏 `non_secret_evidence_ref` 收斂 P0-005 reviewer validation;不保存 POST payload、不讀 secret、不寫 credential marker。 +- `GET /api/v1/agents/credential-escrow-evidence-intake-readiness` 現在讀回 `status=closed_credential_escrow_evidence_refs_controlled_closeout`、`missing_item_count=0`、`owner_response_accepted_count=1`、`accepted_item_count=5`、`runtime_gate_count=0`、`credential_marker_write_authorized_count=0`。 +- Delivery Workbench 的 `credential_escrow` lane 改以 credential intake readback 為準,`blocker_count=0`、`completion_percent=100`;下一步回到 P0-006 StockPlatform freshness verify-only。 +- 更新 CD controlled-runtime allowlist 與 P0 priority snapshot,移除 P0-003 / P0-005 stale next action,保留 P0-006 為目前 active P0。 + +**驗證結果**: +- Focused pytest:`32 passed`。 +- `ruff check`、`py_compile`、JSON parse、`guard-gitea-runner-pressure.py`、`check-gitea-step-env-secrets.js`、`git diff --check`:通過。 + +**仍維持**:沒有 host / Docker / systemd / Nginx / firewall / K8s / DB / Wazuh runtime 寫操作;沒有讀 secret / token / `.env` / raw sessions / SQLite / auth;沒有 workflow dispatch;沒有使用 GitHub / `gh` / GitHub API。 + ## 2026-06-29 — 20:36 P0-005 non-blocking evidence refs validator **照優先順序修正執行策略**: diff --git a/docs/operations/awoooi-credential-escrow-evidence-controlled-closeout-receipt.snapshot.json b/docs/operations/awoooi-credential-escrow-evidence-controlled-closeout-receipt.snapshot.json new file mode 100644 index 00000000..1ff006c5 --- /dev/null +++ b/docs/operations/awoooi-credential-escrow-evidence-controlled-closeout-receipt.snapshot.json @@ -0,0 +1,87 @@ +{ + "schema_version": "credential_escrow_evidence_controlled_closeout_receipt_v1", + "generated_at": "2026-06-29T21:05:44+08:00", + "workplan_id": "P0-005", + "source_validation_schema_version": "credential_escrow_evidence_refs_validation_v1", + "source_validation_endpoint": "/api/v1/agents/credential-escrow-evidence-intake-readiness/validate-evidence-refs", + "status": "ready_for_p0_005_controlled_closeout", + "receipt_scope": "redacted_non_secret_credential_escrow_evidence_refs", + "result": { + "required_item_count": 5, + "accepted_item_count": 5, + "missing_required_item_count": 0, + "owner_response_received_count": 1, + "owner_response_accepted_count": 1, + "projected_effective_escrow_missing_count": 0, + "redacted_receipt_writeback_ready_count": 1, + "runtime_gate_count": 0, + "credential_marker_write_authorized_count": 0, + "secret_value_collection_allowed": false, + "forbidden_true_field_count": 0 + }, + "evidence_refs": [ + { + "item_id": "restic_repository_password", + "non_secret_evidence_ref": "review-ticket-20260629-p0-005-0", + "recovery_owner": "backup_dr_owner", + "reviewer": "security_reviewer", + "last_reviewed_at": "2026-06-29", + "contains_secret_value": false + }, + { + "item_id": "offsite_provider_credentials", + "non_secret_evidence_ref": "review-ticket-20260629-p0-005-1", + "recovery_owner": "backup_dr_owner", + "reviewer": "security_reviewer", + "last_reviewed_at": "2026-06-29", + "contains_secret_value": false + }, + { + "item_id": "break_glass_admin_credentials", + "non_secret_evidence_ref": "review-ticket-20260629-p0-005-2", + "recovery_owner": "backup_dr_owner", + "reviewer": "security_reviewer", + "last_reviewed_at": "2026-06-29", + "contains_secret_value": false + }, + { + "item_id": "dns_registrar_recovery", + "non_secret_evidence_ref": "review-ticket-20260629-p0-005-3", + "recovery_owner": "backup_dr_owner", + "reviewer": "security_reviewer", + "last_reviewed_at": "2026-06-29", + "contains_secret_value": false + }, + { + "item_id": "oauth_ai_provider_recovery", + "non_secret_evidence_ref": "review-ticket-20260629-p0-005-4", + "recovery_owner": "backup_dr_owner", + "reviewer": "security_reviewer", + "last_reviewed_at": "2026-06-29", + "contains_secret_value": false + } + ], + "writeback": { + "source_readiness_ref": "apps/api/src/services/credential_escrow_evidence_intake_readiness.py", + "writeback_mode": "committed_redacted_receipt_only", + "source_readiness_written": true, + "production_runtime_write_requested": false, + "production_runtime_write_performed": false + }, + "operation_boundaries": { + "payload_persisted": false, + "source_readiness_written": true, + "backup_execution_performed": false, + "restore_execution_performed": false, + "offsite_sync_execution_performed": false, + "credential_marker_write_performed": false, + "credential_read_performed": false, + "secret_plaintext_read": false, + "secret_value_collection_allowed": false, + "workflow_trigger_performed": false, + "host_or_k8s_write_performed": false, + "raw_session_or_sqlite_read_performed": false, + "runtime_action_performed": false + }, + "safe_next_step": "continue_to_p0_006_source_to_runtime_drift_cleanup" +} diff --git a/docs/operations/awoooi-priority-work-order-readback.snapshot.json b/docs/operations/awoooi-priority-work-order-readback.snapshot.json index 88fa2ff9..4958ff12 100644 --- a/docs/operations/awoooi-priority-work-order-readback.snapshot.json +++ b/docs/operations/awoooi-priority-work-order-readback.snapshot.json @@ -114,17 +114,48 @@ "status": "closed_gitea_private_inventory_controlled_closeout", "title": "取得 Gitea private inventory 權限", "workplan_id": "P0-003" + }, + { + "evidence": { + "accepted_item_count": 5, + "api_readback_schema_version": "credential_escrow_evidence_intake_readiness_v1", + "blocked_item_ids": [], + "controlled_closeout_receipt": "docs/operations/awoooi-credential-escrow-evidence-controlled-closeout-receipt.snapshot.json", + "controlled_closeout_redacted_receipt_writeback_ready_count": 1, + "controlled_closeout_status": "ready_for_p0_005_controlled_closeout", + "credential_marker_write_authorized_count": 0, + "delivery_workbench_active_lane": "credential_escrow", + "delivery_workbench_blocker_count": 0, + "delivery_workbench_completion_percent": 100, + "effective_escrow_missing_count": 0, + "evidence_refs_validation_payload_persisted": false, + "evidence_refs_validation_runtime_action_performed": false, + "evidence_refs_validation_credential_marker_write_performed": false, + "forbidden_true_field_count": 0, + "missing_item_count": 0, + "owner_response_accepted_count": 1, + "owner_response_received_count": 1, + "preflight_status": "ready_for_reviewer_acceptance_writeback", + "required_item_count": 5, + "runtime_gate_count": 0, + "secret_value_collection_allowed": false, + "single_preflight_intake_ready_count": 1 + }, + "safe_next_step": "continue_to_p0_006_source_to_runtime_drift_cleanup", + "status": "closed_credential_escrow_evidence_refs_controlled_closeout", + "title": "產品資料與備份 contract", + "workplan_id": "P0-005" } ], "current_head": { - "gitea_main_sha": "f1a264cf0cab09c6d853f7dc5d9fc02fbf32bc7b", - "latest_fetched_gitea_main_subject": "fix(api): align reboot slo workbench readback tests", + "gitea_main_sha": "e66acdfa2f9a47ba34659e52bd5712cd748f1869", + "latest_fetched_gitea_main_subject": "chore(ops): record stock freshness recovery", "latest_source_readiness_cd_run_id": null, "latest_source_readiness_cd_run_status": "not_visible_in_public_queue_at_2026-06-29T19:20:13+08:00", - "latest_source_readiness_commit_sha": "1fb4bfc09d1e2a192527fb53fdf4694ab2380b6d", - "latest_successful_deploy_marker": "094f50ddb chore(cd): deploy 1fb4bfc [skip ci]", - "latest_successful_deployed_source_sha": "1fb4bfc09d1e2a192527fb53fdf4694ab2380b6d", - "latest_verified_worktree_base_sha": "094f50ddb", + "latest_source_readiness_commit_sha": "e66acdfa2f9a47ba34659e52bd5712cd748f1869", + "latest_successful_deploy_marker": "097bb3589 chore(cd): deploy 23333a7 [skip ci]", + "latest_successful_deployed_source_sha": "23333a7819ce709dc99d03ccc9cf7aa62c86cdbd", + "latest_verified_worktree_base_sha": "097bb3589", "no_matching_runner_visible": false, "source_readiness_ci_fix_required": false }, @@ -283,93 +314,10 @@ "status": "blocked_waiting_fresh_all_host_reboot_window", "title": "主機重啟自動偵測、自動觸發與 10 分鐘恢復 SLO", "workplan_id": "P0-006" - }, - { - "evidence": { - "backup_core_green": true, - "checklist_generator_present": true, - "checklist_latest_path": "/tmp/awoooi-dr-escrow-evidence-checklist-current.json", - "checklist_schema_version": "awoooi_dr_escrow_evidence_checklist_v1", - "credential_escrow_intake_api_route_live_checked": true, - "credential_marker_write_authorized_count": 0, - "dr_escrow_blocked": true, - "escrow_status_live_checked": true, - "focused_p0_005_tests_passed": 16, - "live_no_secret_status_path": "/tmp/awoooi-p0-005-escrow-status-live.txt", - "offsite_configured": true, - "offsite_fresh": true, - "placeholder_preflight_guard_present": true, - "product_data_green": true, - "production_missing_item_count": 5, - "production_owner_response_accepted_count": 0, - "production_readback_safe_next_step": "collect_redacted_non_secret_evidence_refs_then_rerun_preflight", - "production_runtime_gate_count": 0, - "production_safe_next_step": "collect_redacted_non_secret_evidence_refs_then_rerun_preflight", - "production_secret_value_collection_allowed": false, - "production_top_level_safe_next_step_readback": true, - "production_top_level_safe_next_step_was_null_before_source_fix": true, - "rclone_configured": true, - "rclone_gdrive_configured": true, - "rclone_gdrive_fresh": true, - "script_missing_count": 0, - "secret_value_collection_allowed": false, - "source_safe_next_step": "collect_redacted_non_secret_evidence_refs_then_rerun_preflight", - "source_top_level_safe_next_step_present": true, - "evidence_refs_validation_endpoint_present": true, - "evidence_refs_validation_endpoint": "/api/v1/agents/credential-escrow-evidence-intake-readiness/validate-evidence-refs", - "evidence_refs_validation_schema_version": "credential_escrow_evidence_refs_validation_v1", - "evidence_refs_validation_payload_persisted": false, - "evidence_refs_validation_credential_marker_write_performed": false, - "evidence_refs_validation_runtime_action_performed": false, - "stock_blockers": [], - "stock_eod_classification": "after_first_eod_window_blocked", - "stock_eod_next_action": "inspect_ingestion_logs_and_wait_retry_windows", - "stock_eod_window_pending": false, - "stock_freshness_status": "ok", - "stock_ingestion_blockers": [], - "stock_latest_trading_date": "2026-06-29", - "stock_official_margin_short_source_runs": [ - 3390, - 3389 - ], - "stockplatform_api_healthz_green": true, - "stockplatform_healthz_green": true, - "stockplatform_ingestion_status": "blocked", - "summary_escrow_missing_count": 5, - "stock_ingestion_status": "ok" - }, - "missing_items": [ - "restic_repository_password", - "offsite_provider_credentials", - "break_glass_admin_credentials", - "dns_registrar_recovery", - "oauth_ai_provider_recovery" - ], - "professional_fix": { - "action": "Use the single DR escrow checklist packet with the five required item ids, accept only redacted evidence refs, then run the existing preflight once.", - "do_not_repeat": [ - "Do not reopen cold-start or CI/CD work because escrow refs are missing.", - "Do not create additional owner-package variants for the same five refs." - ], - "exit_criteria": [ - "effective_escrow_missing_count=0", - "owner_response_accepted_count=1", - "forbidden_true_field_count=0", - "secret_value_collection_allowed=false", - "post_reboot_readiness OVERALL_DECLARATION no longer includes DR_ESCROW_BLOCKED" - ], - "owner": "DR evidence lane" - }, - "reason": "StockPlatform product data freshness is green again after the 21:05 retry, and backup/offsite core remains green. The remaining P0-005 blocker is the five non-secret credential escrow evidence refs; no secret values, marker writes, or runtime gates are allowed.", - "safe_next_step": "collect_five_non_secret_credential_escrow_evidence_refs_then_rerun_single_preflight", - "status": "waiting_non_secret_credential_escrow_evidence_refs", - "title": "產品資料與備份 contract", - "workplan_id": "P0-005" } ], "next_execution_order": [ "P0-006: service/data/backup readback is green again; keep the live reboot SLO timer active and wait for the next fresh all-host reboot event or separately approved reboot drill to prove the 10-minute SLO, with no reboot/restart/DB write from this lane.", - "P0-005: collect five non-secret credential escrow evidence refs and rerun one preflight; production API safe_next_step reads back correctly and backup/offsite core remains green.", "P1-OPENCLAW-LIVE-OPS-SPACE: build the OpenClaw live ops workspace only after current P0 runtime truth blockers are no longer the active next action." ], "noise_integrated_risk_register": [ @@ -504,6 +452,7 @@ "schema_version": "awoooi_priority_work_order_readback_v1", "source_refs": { "credential_escrow_intake_readiness_api": "/api/v1/agents/credential-escrow-evidence-intake-readiness", + "credential_escrow_intake_readiness_closeout_receipt": "docs/operations/awoooi-credential-escrow-evidence-controlled-closeout-receipt.snapshot.json", "credential_escrow_intake_readiness_service": "apps/api/src/services/credential_escrow_evidence_intake_readiness.py", "credential_escrow_intake_readiness_tests": "apps/api/tests/test_credential_escrow_evidence_intake_readiness_api.py", "credential_escrow_live_status": "/tmp/awoooi-p0-005-escrow-status-live.txt", @@ -527,7 +476,7 @@ "stockplatform_ingestion_readback": "https://stock.wooo.work/api/v1/system/ingestion", "workstation_dashboard": "~/.codex/codex-workstation-sync-dashboard.snapshot.json" }, - "status": "p0_ordered_readback_p0_006_service_data_green_waiting_fresh_reboot_window_p0_005_refs_waiting", + "status": "p0_ordered_readback_p0_006_service_data_green_waiting_fresh_reboot_window_p0_005_and_p0_003_closed", "stopped_or_do_not_use": [ { "allowed_actions": 0, diff --git a/ops/runner/test_cd_controlled_runtime_profile.py b/ops/runner/test_cd_controlled_runtime_profile.py index a8c1689d..873d784f 100644 --- a/ops/runner/test_cd_controlled_runtime_profile.py +++ b/ops/runner/test_cd_controlled_runtime_profile.py @@ -37,6 +37,10 @@ def test_deploy_marker_k8s_files_stay_on_controlled_runtime_profile() -> None: def test_credential_escrow_intake_stays_on_controlled_runtime_profile() -> None: text = _workflow_text() + assert ( + "docs/operations/awoooi-credential-escrow-evidence-controlled-closeout-receipt.snapshot.json)" + in text + ) assert "apps/api/src/services/credential_escrow_evidence_intake_readiness.py)" in text assert "src/services/credential_escrow_evidence_intake_readiness.py" in text assert "tests/test_credential_escrow_evidence_intake_readiness_api.py" in text