From 60d51d40da939b24102da34f979967afc444b8ea Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 29 Jun 2026 16:46:42 +0800 Subject: [PATCH] feat(api): expose credential escrow delivery lane --- .../services/delivery_closure_workbench.py | 83 ++++++++++++++++++- .../test_delivery_closure_workbench_api.py | 27 ++++++ docs/LOGBOOK.md | 18 ++++ 3 files changed, 127 insertions(+), 1 deletion(-) diff --git a/apps/api/src/services/delivery_closure_workbench.py b/apps/api/src/services/delivery_closure_workbench.py index cacacee9..42c3987b 100644 --- a/apps/api/src/services/delivery_closure_workbench.py +++ b/apps/api/src/services/delivery_closure_workbench.py @@ -86,6 +86,25 @@ def build_delivery_closure_workbench( private_inventory_blockers = _int( private_inventory_rollups.get("active_blocker_count") ) + credential_escrow_required_items = _int( + backup_rollups.get("credential_escrow_required_item_count") + ) + credential_escrow_missing_items = _int( + backup_rollups.get("credential_escrow_effective_missing_count") + ) + credential_escrow_completion = _percent( + ( + (credential_escrow_required_items - credential_escrow_missing_items) + / max(credential_escrow_required_items, 1) + ) + * 100 + ) + backup_blocked_row_ids = _strings(backup_rollups.get("blocked_row_ids")) + backup_non_credential_blockers = [ + row_id + for row_id in backup_blocked_row_ids + if row_id != "credential_escrow_markers" + ] runtime_action_required = set( _strings(runtime_rollups.get("action_required_surface_ids")) ) @@ -358,6 +377,66 @@ def build_delivery_closure_workbench( "href": "/deployments", "next_action": _first_string(production_deploy.get("next_actions")), }, + { + "id": "credential_escrow", + "source_id": "backup_dr_credential_escrow", + "completion_percent": credential_escrow_completion, + "status": str( + backup_rollups.get("credential_escrow_intake_status") + or "blocked_waiting_non_secret_credential_escrow_evidence" + ), + "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 "" + ), + "owner_response_received_count": _int( + backup_rollups.get( + "credential_escrow_owner_response_received_count" + ) + ), + "owner_response_accepted_count": _int( + backup_rollups.get( + "credential_escrow_owner_response_accepted_count" + ) + ), + "runtime_gate_count": _int( + backup_rollups.get("credential_escrow_runtime_gate_count") + ), + "secret_value_collection_allowed": ( + backup_rollups.get( + "credential_escrow_secret_value_collection_allowed" + ) + is True + ), + "credential_marker_write_authorized_count": _int( + backup_rollups.get("credential_marker_write_authorized_count") + ), + "forbidden_true_field_count": _int( + backup_rollups.get("credential_escrow_forbidden_true_field_count") + ), + "scorecard_schema_version": str( + backup_rollups.get( + "credential_escrow_intake_scorecard_schema_version" + ) + or "" + ), + "scorecard_verifier": str( + backup_rollups.get("credential_escrow_intake_scorecard_verifier") + or "" + ), + }, + "href": "/operations", + "next_action": "collect_redacted_non_secret_evidence_refs_then_rerun_preflight", + }, { "id": "gitea_private_inventory", "source_id": "gitea_private_inventory_p0_scorecard", @@ -498,10 +577,12 @@ def build_delivery_closure_workbench( backup_status.get("overall_completion_percent") ), "status": str(backup_status.get("current_task_id") or "unknown"), - "blocker_count": len(_strings(backup_rollups.get("blocked_row_ids"))), + "blocker_count": len(backup_non_credential_blockers), "metric": { "kind": "readiness_row_count", "rows": _int(backup_rollups.get("total_rows")), + "blocked_row_ids": backup_blocked_row_ids, + "non_credential_blocked_row_ids": backup_non_credential_blockers, "credential_escrow_intake_scorecard_schema_version": str( backup_rollups.get( "credential_escrow_intake_scorecard_schema_version" diff --git a/apps/api/tests/test_delivery_closure_workbench_api.py b/apps/api/tests/test_delivery_closure_workbench_api.py index ff2a5e7f..a9574613 100644 --- a/apps/api/tests/test_delivery_closure_workbench_api.py +++ b/apps/api/tests/test_delivery_closure_workbench_api.py @@ -56,6 +56,32 @@ def test_delivery_closure_workbench_uses_gitea_private_inventory_lane(): ) +def test_delivery_closure_workbench_exposes_p0_005_credential_escrow_lane(): + payload = load_delivery_closure_workbench() + + _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["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"]["runtime_gate_count"] == 0 + assert lane["metric"]["secret_value_collection_allowed"] is False + assert lane["metric"]["credential_marker_write_authorized_count"] == 0 + assert lane["next_action"] == ( + "collect_redacted_non_secret_evidence_refs_then_rerun_preflight" + ) + + def _assert_delivery_workbench_shape(data: dict): assert data["schema_version"] == "delivery_closure_workbench_v1" assert data["summary"]["source_count"] == 7 @@ -115,6 +141,7 @@ def _assert_delivery_workbench_shape(data: dict): assert lane_ids == { "release", "production_deploy", + "credential_escrow", "gitea_private_inventory", "cicd_baseline", "gitea", diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 60dbba93..9c38dc6b 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -49519,6 +49519,24 @@ production browser smoke: - 沒有讀 secret / token / `.env` / raw sessions / SQLite / auth,沒有寫 credential marker。 - 沒有使用 GitHub / gh / GitHub API / GitHub Actions。 +## 2026-06-29 — 16:45 P0-003 deploy readback 與 P0-005 Workbench lane 整合 + +**完成內容**: +- Gitea CD #3923 已成功,deploy marker `3ccfd53b3 chore(cd): deploy 7219a48 [skip ci]` 已 fast-forward 到本地 worktree。 +- Production GET `/api/v1/agents/gitea-private-inventory-p0-scorecard` 已讀回 HTTP 200、`schema_version=gitea_private_inventory_p0_scorecard_readback_v1`、`priority=P0-003`。 +- Production GET `/api/v1/agents/delivery-closure-workbench` 已讀回 GitHub lane `stopped_retired_do_not_use`、`github_blocked_preflight_target_count=0`,Gitea private inventory lane 維持 active P0 blocker。 +- 新增 Delivery Workbench `credential_escrow` lane,將 P0-005 五個 non-secret evidence item blockers 從 generic backup lane 拆出為第一級 product lane。 + +**本地驗證結果**: +- `python3.11 -m py_compile apps/api/src/services/delivery_closure_workbench.py apps/api/tests/test_delivery_closure_workbench_api.py`:通過。 +- `DATABASE_URL=postgresql+asyncpg://ci:ci@localhost/ci PYTHONFAULTHANDLER=1 python3.11 -m pytest apps/api/tests/test_delivery_closure_workbench_api.py apps/api/tests/test_credential_escrow_evidence_intake_readiness_api.py apps/api/tests/test_gitea_private_inventory_p0_scorecard_api.py -q --tb=short -x -p no:cacheprovider`:`15 passed`。 +- `git diff --check`:通過。 + +**仍維持**: +- 沒有讀 secret / token / `.env` / raw sessions / SQLite / auth,沒有寫 credential marker。 +- 沒有重啟主機,沒有 restart Docker / Nginx / K3s / DB / firewall,沒有修改 runner/host 權限。 +- 沒有使用 GitHub / gh / GitHub API / GitHub Actions。 + ## 2026-06-29 — 15:34 P0-005 credential escrow readiness safe next step source fix **照優先順序完成的讀回**: