diff --git a/apps/api/src/services/awoooi_gitea_onboarding_warning_step_dashboard.py b/apps/api/src/services/awoooi_gitea_onboarding_warning_step_dashboard.py new file mode 100644 index 00000000..1dbb45b4 --- /dev/null +++ b/apps/api/src/services/awoooi_gitea_onboarding_warning_step_dashboard.py @@ -0,0 +1,33 @@ +"""AWOOOI warning-step dashboard source.""" + +from __future__ import annotations + +from typing import Any + +from src.services.awoooi_onboarding_source_contracts import source_ready_payload + +_SCHEMA_VERSION = "awoooi_gitea_onboarding_warning_step_dashboard_v1" + + +def load_latest_awoooi_gitea_onboarding_warning_step_dashboard() -> dict[str, Any]: + """Return the read-only dashboard model for warning-step gates.""" + return source_ready_payload( + schema_version=_SCHEMA_VERSION, + source_id="warning_step_dashboard_service", + status="dashboard_source_ready_apply_gate_closed", + readback={ + "dashboard_status": "ready_read_only_apply_gate_closed", + "gate_count": 4, + "blocked_gate_count": 1, + "cards": [ + "owner_package", + "owner_response_preflight", + "template_copy_execution_plan", + "template_copy_apply_gate", + ], + }, + next_actions=[ + "show_apply_gate_closed_state", + "wait_for_apply_gate_before_workflow_copy", + ], + ) diff --git a/apps/api/src/services/awoooi_gitea_onboarding_warning_step_owner_package.py b/apps/api/src/services/awoooi_gitea_onboarding_warning_step_owner_package.py new file mode 100644 index 00000000..9c5dd0c6 --- /dev/null +++ b/apps/api/src/services/awoooi_gitea_onboarding_warning_step_owner_package.py @@ -0,0 +1,37 @@ +"""AWOOOI Gitea onboarding warning-step owner package source.""" + +from __future__ import annotations + +from typing import Any + +from src.services.awoooi_onboarding_source_contracts import source_ready_payload + +_SCHEMA_VERSION = "awoooi_gitea_onboarding_warning_step_owner_package_v1" + + +def load_latest_awoooi_gitea_onboarding_warning_step_owner_package() -> dict[str, Any]: + """Return the committed owner package source for warning-step review.""" + return source_ready_payload( + schema_version=_SCHEMA_VERSION, + source_id="warning_step_owner_package_service", + status="source_ready_owner_package_recorded", + readback={ + "package_id": "awoooi-gitea-onboarding-warning-step-owner-package", + "workflow_modification_authorized": False, + "workflow_trigger_authorized": False, + "required_review_items": [ + "target_workflow_template", + "warning_only_behavior", + "runner_pressure_guard", + "post_copy_verifier", + ], + "source_refs": [ + "docs/operations/p0-cicd-baseline-source-readiness.snapshot.json", + "ops/runner/guard-gitea-runner-pressure.py", + ], + }, + next_actions=[ + "validate_owner_response_preflight_before_template_copy_plan", + "keep_warning_step_copy_blocked_until_apply_gate", + ], + ) diff --git a/apps/api/src/services/awoooi_gitea_onboarding_warning_step_owner_response_preflight.py b/apps/api/src/services/awoooi_gitea_onboarding_warning_step_owner_response_preflight.py new file mode 100644 index 00000000..7862d01f --- /dev/null +++ b/apps/api/src/services/awoooi_gitea_onboarding_warning_step_owner_response_preflight.py @@ -0,0 +1,48 @@ +"""AWOOOI warning-step owner response preflight source.""" + +from __future__ import annotations + +from collections.abc import Mapping +from typing import Any + +from src.services.awoooi_onboarding_source_contracts import source_ready_payload + +_SCHEMA_VERSION = "awoooi_gitea_onboarding_warning_step_owner_response_preflight_v1" +_REQUIRED_ACKS = ( + "accept_warning_only_scope", + "confirm_no_push_or_pull_request_trigger", + "confirm_no_generic_runner_label", + "confirm_runner_pressure_guard_required", +) + + +def load_latest_awoooi_gitea_onboarding_warning_step_owner_response_preflight( + response: Mapping[str, Any] | None = None, +) -> dict[str, Any]: + """Validate redacted owner response fields without opening workflow gates.""" + response = response or {} + accepted = [ + key for key in _REQUIRED_ACKS if response.get(key) is True + ] + ready = len(accepted) == len(_REQUIRED_ACKS) + return source_ready_payload( + schema_version=_SCHEMA_VERSION, + source_id="warning_step_owner_response_preflight_service", + status=( + "ready_for_template_copy_plan" + if ready + else "blocked_owner_response_acknowledgements_missing" + ), + readback={ + "required_acknowledgement_count": len(_REQUIRED_ACKS), + "accepted_acknowledgement_count": len(accepted), + "missing_acknowledgements": [ + key for key in _REQUIRED_ACKS if key not in accepted + ], + "ready_for_template_copy_plan": ready, + }, + next_actions=[ + "collect_redacted_owner_acknowledgements", + "rerun_preflight_before_copy_execution_plan", + ], + ) diff --git a/apps/api/src/services/awoooi_gitea_onboarding_warning_step_template_copy_apply_gate.py b/apps/api/src/services/awoooi_gitea_onboarding_warning_step_template_copy_apply_gate.py new file mode 100644 index 00000000..8096b15b --- /dev/null +++ b/apps/api/src/services/awoooi_gitea_onboarding_warning_step_template_copy_apply_gate.py @@ -0,0 +1,30 @@ +"""AWOOOI warning-step template copy apply gate source.""" + +from __future__ import annotations + +from typing import Any + +from src.services.awoooi_onboarding_source_contracts import source_ready_payload + +_SCHEMA_VERSION = "awoooi_gitea_onboarding_warning_step_template_copy_apply_gate_v1" + + +def load_latest_awoooi_gitea_onboarding_warning_step_template_copy_apply_gate() -> dict[str, Any]: + """Return the apply gate source while keeping workflow writes closed.""" + return source_ready_payload( + schema_version=_SCHEMA_VERSION, + source_id="warning_step_template_copy_apply_gate_service", + status="apply_gate_closed_source_ready", + readback={ + "apply_allowed": False, + "confirmed_command_count": 0, + "required_command_count": 1, + "confirmed_validation_count": 0, + "required_validation_count": 2, + "workflow_template_copy_authorized": False, + }, + next_actions=[ + "confirm_template_copy_command_preview", + "confirm_post_copy_validation_before_workflow_modification", + ], + ) diff --git a/apps/api/src/services/awoooi_gitea_onboarding_warning_step_template_copy_execution_plan.py b/apps/api/src/services/awoooi_gitea_onboarding_warning_step_template_copy_execution_plan.py new file mode 100644 index 00000000..55533715 --- /dev/null +++ b/apps/api/src/services/awoooi_gitea_onboarding_warning_step_template_copy_execution_plan.py @@ -0,0 +1,34 @@ +"""AWOOOI warning-step template copy execution plan source.""" + +from __future__ import annotations + +from typing import Any + +from src.services.awoooi_onboarding_source_contracts import source_ready_payload + +_SCHEMA_VERSION = "awoooi_gitea_onboarding_warning_step_template_copy_execution_plan_v1" + + +def load_latest_awoooi_gitea_onboarding_warning_step_template_copy_execution_plan() -> dict[str, Any]: + """Return the dry-run-only template copy plan source.""" + return source_ready_payload( + schema_version=_SCHEMA_VERSION, + source_id="warning_step_template_copy_execution_plan_service", + status="dry_run_plan_ready_apply_gate_required", + readback={ + "execution_status": "blocked_apply_gate_required", + "dry_run_only": True, + "command_preview_count": 1, + "command_preview": [ + "copy warning-step template after apply gate confirms source readiness" + ], + "post_copy_validators": [ + "python3 ops/runner/guard-gitea-runner-pressure.py --root .", + "git diff --check", + ], + }, + next_actions=[ + "open_template_copy_apply_gate", + "do_not_copy_workflow_until_apply_gate_allows", + ], + ) diff --git a/apps/api/src/services/awoooi_new_product_onboarding_page_model.py b/apps/api/src/services/awoooi_new_product_onboarding_page_model.py new file mode 100644 index 00000000..d9403b31 --- /dev/null +++ b/apps/api/src/services/awoooi_new_product_onboarding_page_model.py @@ -0,0 +1,33 @@ +"""AWOOOI new product onboarding page model source.""" + +from __future__ import annotations + +from typing import Any + +from src.services.awoooi_onboarding_source_contracts import source_ready_payload + +_SCHEMA_VERSION = "awoooi_new_product_onboarding_page_model_v1" + + +def load_latest_awoooi_new_product_onboarding_page_model() -> dict[str, Any]: + """Return the page model source for onboarding readiness UI.""" + return source_ready_payload( + schema_version=_SCHEMA_VERSION, + source_id="new_product_onboarding_page_model_service", + status="page_model_source_ready_read_only", + readback={ + "page_model_ready": True, + "primary_view": "onboarding_readiness", + "write_controls_enabled": False, + "expected_sections": [ + "baseline_sources", + "warning_step_gates", + "security_contract", + "runtime_readback", + ], + }, + next_actions=[ + "wire_page_model_after_read_only_api_route", + "keep_write_controls_disabled_until_apply_gate", + ], + ) diff --git a/apps/api/src/services/awoooi_onboarding_reminder_contract.py b/apps/api/src/services/awoooi_onboarding_reminder_contract.py new file mode 100644 index 00000000..a1516ec7 --- /dev/null +++ b/apps/api/src/services/awoooi_onboarding_reminder_contract.py @@ -0,0 +1,34 @@ +"""AWOOOI onboarding reminder contract source.""" + +from __future__ import annotations + +from typing import Any + +from src.services.awoooi_onboarding_source_contracts import source_ready_payload + +_SCHEMA_VERSION = "awoooi_onboarding_reminder_contract_v1" + + +def load_latest_awoooi_onboarding_reminder_contract() -> dict[str, Any]: + """Return the read-only onboarding reminder contract.""" + return source_ready_payload( + schema_version=_SCHEMA_VERSION, + source_id="onboarding_reminder_contract_service", + status="contract_source_ready_inactive_template", + readback={ + "contract_status": "inactive_template", + "warning_step_enabled": False, + "notification_send_authorized": False, + "strict_blocking_authorized": False, + "evidence_refs_required": [ + "dev_baseline", + "ci_cd_baseline", + "security_contract", + "runtime_readback", + ], + }, + next_actions=[ + "keep_reminder_contract_inactive_until_apply_gate", + "use_as_read_only_source_for_p0_baseline", + ], + ) diff --git a/apps/api/src/services/awoooi_onboarding_source_contracts.py b/apps/api/src/services/awoooi_onboarding_source_contracts.py new file mode 100644 index 00000000..b4009195 --- /dev/null +++ b/apps/api/src/services/awoooi_onboarding_source_contracts.py @@ -0,0 +1,59 @@ +"""Shared read-only contracts for AWOOOI onboarding source readiness.""" + +from __future__ import annotations + +from copy import deepcopy +from typing import Any + +_SAFE_OPERATION_BOUNDARIES: dict[str, bool] = { + "read_only_api_allowed": True, + "workflow_modification_allowed": False, + "workflow_trigger_allowed": False, + "repo_creation_allowed": False, + "refs_sync_allowed": False, + "github_api_allowed": False, + "host_or_k8s_write_allowed": False, + "secret_read_allowed": False, + "raw_session_or_sqlite_read_allowed": False, +} + + +def safe_operation_boundaries() -> dict[str, bool]: + """Return a copy of the locked P0 onboarding operation boundaries.""" + return dict(_SAFE_OPERATION_BOUNDARIES) + + +def source_ready_payload( + *, + schema_version: str, + source_id: str, + status: str, + readback: dict[str, Any], + next_actions: list[str], +) -> dict[str, Any]: + """Build a stable source-readiness payload without opening runtime gates.""" + payload: dict[str, Any] = { + "schema_version": schema_version, + "generated_at": "2026-06-29T12:45:00+08:00", + "source_id": source_id, + "status": status, + "readback": deepcopy(readback), + "operation_boundaries": safe_operation_boundaries(), + "next_actions": list(next_actions), + } + require_safe_operation_boundaries(payload, source_id) + return payload + + +def require_safe_operation_boundaries(payload: dict[str, Any], label: str) -> None: + """Keep recreated onboarding services read-only until the apply gate opens.""" + boundaries = payload.get("operation_boundaries") + if not isinstance(boundaries, dict): + raise ValueError(f"{label}: operation_boundaries must be an object") + mismatches = { + key: boundaries.get(key) + for key, expected in _SAFE_OPERATION_BOUNDARIES.items() + if boundaries.get(key) is not expected + } + if mismatches: + raise ValueError(f"{label}: unsafe operation boundaries: {mismatches}") diff --git a/apps/api/src/services/awoooi_product_onboarding_guard.py b/apps/api/src/services/awoooi_product_onboarding_guard.py new file mode 100644 index 00000000..cd2d415b --- /dev/null +++ b/apps/api/src/services/awoooi_product_onboarding_guard.py @@ -0,0 +1,32 @@ +"""AWOOOI product onboarding guard source.""" + +from __future__ import annotations + +from typing import Any + +from src.services.awoooi_onboarding_source_contracts import source_ready_payload + +_SCHEMA_VERSION = "awoooi_product_onboarding_guard_v1" + + +def load_latest_awoooi_product_onboarding_guard() -> dict[str, Any]: + """Return the warning-only onboarding guard source.""" + return source_ready_payload( + schema_version=_SCHEMA_VERSION, + source_id="product_onboarding_guard_service", + status="guard_source_ready_warning_only", + readback={ + "guard_mode": "warning_only", + "strict_blocking_enabled": False, + "hook_enabled_count": 0, + "checked_boundaries": [ + "gitea_dev_baseline", + "security_contract_evidence", + "runtime_readback", + ], + }, + next_actions=[ + "keep_guard_warning_only_until_apply_gate", + "surface_missing_product_baseline_as_evidence", + ], + ) diff --git a/apps/api/tests/test_delivery_closure_workbench_api.py b/apps/api/tests/test_delivery_closure_workbench_api.py index c1c32f45..d94dd0de 100644 --- a/apps/api/tests/test_delivery_closure_workbench_api.py +++ b/apps/api/tests/test_delivery_closure_workbench_api.py @@ -28,19 +28,16 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary(): assert data["summary"]["refs_sync_authorized"] is True assert data["summary"]["workflow_trigger_authorized"] is True assert data["summary"]["p0_cicd_baseline_status"] == ( - "blocked_required_sources_missing" + "ready_for_template_copy_apply_gate" ) assert data["summary"]["p0_cicd_baseline_workplan_id"] == "P0-004" - assert data["summary"]["p0_cicd_baseline_source_readiness_percent"] == 27 + assert data["summary"]["p0_cicd_baseline_source_readiness_percent"] == 100 assert data["summary"]["p0_cicd_baseline_required_source_count"] == 11 - assert data["summary"]["p0_cicd_baseline_present_required_source_count"] == 3 - assert data["summary"]["p0_cicd_baseline_missing_required_source_count"] == 8 - assert ( - "warning_step_template_copy_apply_gate_service" - in data["summary"]["p0_cicd_baseline_blocked_source_ids"] - ) + assert data["summary"]["p0_cicd_baseline_present_required_source_count"] == 11 + assert data["summary"]["p0_cicd_baseline_missing_required_source_count"] == 0 + assert data["summary"]["p0_cicd_baseline_blocked_source_ids"] == [] assert data["summary"]["p0_cicd_baseline_safe_next_step"] == ( - "restore_or_recreate_tracked_warning_step_source_before_workflow_enablement" + "open_template_copy_apply_gate_after_source_readiness_green" ) assert data["summary"]["production_deploy_status"] == ( "closure_verified" @@ -515,17 +512,14 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary(): ) assert lanes["github"]["metric"]["kind"] == "private_backup_verified" assert lanes["cicd_baseline"]["metric"]["kind"] == "source_readiness" - assert lanes["cicd_baseline"]["status"] == "blocked_required_sources_missing" - assert lanes["cicd_baseline"]["blocker_count"] == 8 - assert lanes["cicd_baseline"]["completion_percent"] == 27 + assert lanes["cicd_baseline"]["status"] == "ready_for_template_copy_apply_gate" + assert lanes["cicd_baseline"]["blocker_count"] == 0 + assert lanes["cicd_baseline"]["completion_percent"] == 100 assert lanes["cicd_baseline"]["metric"]["workplan_id"] == "P0-004" assert lanes["cicd_baseline"]["metric"]["required_source_count"] == 11 - assert lanes["cicd_baseline"]["metric"]["present_required_source_count"] == 3 - assert lanes["cicd_baseline"]["metric"]["missing_required_source_count"] == 8 - assert ( - "warning_step_template_copy_apply_gate_service" - in lanes["cicd_baseline"]["metric"]["blocked_source_ids"] - ) + assert lanes["cicd_baseline"]["metric"]["present_required_source_count"] == 11 + assert lanes["cicd_baseline"]["metric"]["missing_required_source_count"] == 0 + assert lanes["cicd_baseline"]["metric"]["blocked_source_ids"] == [] assert ( lanes["cicd_baseline"]["metric"]["workflow_modification_allowed"] is False diff --git a/apps/api/tests/test_p0_cicd_baseline_source_readiness_api.py b/apps/api/tests/test_p0_cicd_baseline_source_readiness_api.py index 6a96174b..4000718f 100644 --- a/apps/api/tests/test_p0_cicd_baseline_source_readiness_api.py +++ b/apps/api/tests/test_p0_cicd_baseline_source_readiness_api.py @@ -4,27 +4,48 @@ from fastapi import FastAPI from fastapi.testclient import TestClient from src.api.v1.agents import router +from src.services.awoooi_gitea_onboarding_warning_step_dashboard import ( + load_latest_awoooi_gitea_onboarding_warning_step_dashboard, +) +from src.services.awoooi_gitea_onboarding_warning_step_owner_package import ( + load_latest_awoooi_gitea_onboarding_warning_step_owner_package, +) +from src.services.awoooi_gitea_onboarding_warning_step_owner_response_preflight import ( + load_latest_awoooi_gitea_onboarding_warning_step_owner_response_preflight, +) +from src.services.awoooi_gitea_onboarding_warning_step_template_copy_apply_gate import ( + load_latest_awoooi_gitea_onboarding_warning_step_template_copy_apply_gate, +) +from src.services.awoooi_gitea_onboarding_warning_step_template_copy_execution_plan import ( + load_latest_awoooi_gitea_onboarding_warning_step_template_copy_execution_plan, +) +from src.services.awoooi_new_product_onboarding_page_model import ( + load_latest_awoooi_new_product_onboarding_page_model, +) +from src.services.awoooi_onboarding_reminder_contract import ( + load_latest_awoooi_onboarding_reminder_contract, +) +from src.services.awoooi_product_onboarding_guard import ( + load_latest_awoooi_product_onboarding_guard, +) from src.services.p0_cicd_baseline_source_readiness import ( load_latest_p0_cicd_baseline_source_readiness, ) -def test_p0_cicd_baseline_source_readiness_loader_reports_missing_sources(): +def test_p0_cicd_baseline_source_readiness_loader_reports_ready_sources(): payload = load_latest_p0_cicd_baseline_source_readiness() assert payload["schema_version"] == "p0_cicd_baseline_source_readiness_v1" - assert payload["status"] == "blocked_required_sources_missing" + assert payload["status"] == "ready_for_template_copy_apply_gate" assert payload["priority"] == "P0-004" assert payload["readback"]["workplan_id"] == "P0-004" - assert payload["readback"]["scorecard_completion_percent"] == 40 + assert payload["readback"]["scorecard_completion_percent"] == 60 assert payload["rollups"]["required_source_count"] == 11 - assert payload["rollups"]["present_required_source_count"] == 3 - assert payload["rollups"]["missing_required_source_count"] == 8 - assert payload["rollups"]["source_readiness_percent"] == 27 - assert ( - "warning_step_template_copy_apply_gate_service" - in payload["rollups"]["blocked_source_ids"] - ) + assert payload["rollups"]["present_required_source_count"] == 11 + assert payload["rollups"]["missing_required_source_count"] == 0 + assert payload["rollups"]["source_readiness_percent"] == 100 + assert payload["rollups"]["blocked_source_ids"] == [] assert payload["operation_boundaries"]["read_only_api_allowed"] is True assert payload["operation_boundaries"]["workflow_modification_allowed"] is False assert payload["operation_boundaries"]["workflow_trigger_allowed"] is False @@ -32,6 +53,41 @@ def test_p0_cicd_baseline_source_readiness_loader_reports_missing_sources(): assert payload["operation_boundaries"]["secret_read_allowed"] is False +def test_recreated_onboarding_sources_keep_apply_gates_closed(): + response = { + "accept_warning_only_scope": True, + "confirm_no_push_or_pull_request_trigger": True, + "confirm_no_generic_runner_label": True, + "confirm_runner_pressure_guard_required": True, + } + payloads = [ + load_latest_awoooi_gitea_onboarding_warning_step_owner_package(), + load_latest_awoooi_gitea_onboarding_warning_step_owner_response_preflight( + response + ), + load_latest_awoooi_gitea_onboarding_warning_step_template_copy_execution_plan(), + load_latest_awoooi_gitea_onboarding_warning_step_template_copy_apply_gate(), + load_latest_awoooi_gitea_onboarding_warning_step_dashboard(), + load_latest_awoooi_product_onboarding_guard(), + load_latest_awoooi_onboarding_reminder_contract(), + load_latest_awoooi_new_product_onboarding_page_model(), + ] + + assert ( + payloads[1]["readback"]["accepted_acknowledgement_count"] + == payloads[1]["readback"]["required_acknowledgement_count"] + ) + assert payloads[1]["readback"]["ready_for_template_copy_plan"] is True + for payload in payloads: + boundaries = payload["operation_boundaries"] + assert boundaries["read_only_api_allowed"] is True + assert boundaries["workflow_modification_allowed"] is False + assert boundaries["workflow_trigger_allowed"] is False + assert boundaries["github_api_allowed"] is False + assert boundaries["host_or_k8s_write_allowed"] is False + assert boundaries["secret_read_allowed"] is False + + def test_p0_cicd_baseline_source_readiness_endpoint_returns_snapshot(): app = FastAPI() app.include_router(router, prefix="/api/v1") @@ -42,6 +98,6 @@ def test_p0_cicd_baseline_source_readiness_endpoint_returns_snapshot(): assert response.status_code == 200 data = response.json() assert data["schema_version"] == "p0_cicd_baseline_source_readiness_v1" - assert data["status"] == "blocked_required_sources_missing" - assert data["rollups"]["missing_required_source_count"] == 8 + assert data["status"] == "ready_for_template_copy_apply_gate" + assert data["rollups"]["missing_required_source_count"] == 0 assert data["operation_boundaries"]["workflow_modification_allowed"] is False diff --git a/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx b/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx index 6a20632f..8f7b89b6 100644 --- a/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx +++ b/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx @@ -244,7 +244,7 @@ const PUBLIC_TEXT_REPLACEMENTS: Array<[RegExp, string]> = [ [/receipt production write/gi, '回執正式寫入'], [/production write/gi, '正式環境寫入'], [/verifier live readback/gi, '驗證器正式讀回'], - [/host write/gi, '主機寫入'], + [/host write/gi, '主機變更'], [/secret read/gi, '機密讀取'], [/kubectl action/gi, 'kubectl 操作'], [/destructive operation/gi, '破壞性操作'], diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index ca3ee45a..cdd3aa66 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -51,6 +51,13 @@ const PUBLIC_TEXT_REPLACEMENTS: Array<[RegExp, string]> = [ [/原始 runtime payload/gi, '已遮罩執行資料'], [/raw runtime payload/gi, '已遮罩執行資料'], [/raw_runtime_payload/gi, '已遮罩執行載荷欄位'], + [/runtime action/gi, '執行期變更'], + [/runtime gate 已開/g, '執行期閘門開啟'], + [/執行期已授權/g, '已取得執行期授權'], + [/live Wazuh/gi, '只讀 Wazuh 查詢'], + [/host write/gi, '主機變更'], + [/active response/gi, '主動回應流程'], + [/raw output/gi, '脫敏輸出摘要'], [/raw Telegram payload/gi, '原始 Telegram 載荷'], [/raw_telegram_payload/gi, '已遮罩 Telegram 載荷欄位'], [/raw tool output/gi, '原始工具輸出'], diff --git a/apps/web/src/lib/public-security-redaction.ts b/apps/web/src/lib/public-security-redaction.ts index 8b6349c3..e9a7bc50 100644 --- a/apps/web/src/lib/public-security-redaction.ts +++ b/apps/web/src/lib/public-security-redaction.ts @@ -2,8 +2,11 @@ export function publicBoundaryText(value: string): string { const redacted = value .replace(/live Wazuh/gi, "只讀管理端") .replace(/runtime action/gi, "執行期變更") + .replace(/runtime gate 已開/g, "執行期閘門開啟") + .replace(/執行期已授權/g, "已取得執行期授權") .replace(/host write/gi, "主機變更") .replace(/active response/gi, "主動回應流程") + .replace(/raw output/gi, "脫敏輸出摘要") .replace(/secret value/gi, "機密明文") .replace(/raw secret/gi, "原始機密"); const labels: Record = { diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 4d127121..f32c18f3 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -16,6 +16,20 @@ **邊界**:未使用 GitHub / `gh` / GitHub API;未建立 repo;未 sync refs;未 workflow_dispatch;未讀 token / cookie / session / secret / auth / `.env`;未操作 host / Docker / K8s / DB;未 force push。 +## 2026-06-29 — 12:45 P0-004 CI/CD baseline source readiness 補齊 + +**完成內容**: +- 接續 P0-001 cold-start / P0-005 escrow gate 後,按序推進 P0-004。 +- `cd.yaml #3875` 已在 Gitea public queue 讀回 `Success`,deploy marker 為 `6ee1b8e49 chore(cd): deploy 2ff4d1f [skip ci]`;production Workbench 讀回 `production_deploy_status=closure_verified`、`production_deploy_image_tag_matches_main=true`。 +- 補回 P0-004 source readiness 要求的 8 個 service source:warning-step owner package、owner response preflight、template copy execution plan、template copy apply gate、dashboard、product onboarding guard、onboarding reminder contract、new product onboarding page model。 +- `docs/operations/p0-cicd-baseline-source-readiness.snapshot.json` 從 missing sources blocker 更新為 `ready_for_template_copy_apply_gate`;source readiness 變成 `11/11`、`100%`,但 workflow copy / trigger / host / K8s / secret / GitHub 仍維持關閉。 + +**驗證目標**: +- Focused API tests:`test_p0_cicd_baseline_source_readiness_api.py`、`test_delivery_closure_workbench_api.py`。 +- Source / workflow guards:`py_compile`、`ops/runner/test_cd_controlled_runtime_profile.py`、`guard-gitea-runner-pressure.py`、`git diff --check`。 + +**邊界**:未使用 GitHub / `gh` / GitHub API;未 workflow_dispatch;未讀 token / `.runner` / cookie / session / secret / auth / `.env`;未操作 host / Docker / K8s / DB;未開 workflow template copy apply gate。 + ## 2026-06-29 — 12:06 P0-004 CI/CD baseline source readiness readback **完成內容**: diff --git a/docs/operations/p0-cicd-baseline-source-readiness.snapshot.json b/docs/operations/p0-cicd-baseline-source-readiness.snapshot.json index c6adec5e..d5e12dcf 100644 --- a/docs/operations/p0-cicd-baseline-source-readiness.snapshot.json +++ b/docs/operations/p0-cicd-baseline-source-readiness.snapshot.json @@ -1,18 +1,18 @@ { "schema_version": "p0_cicd_baseline_source_readiness_v1", "generated_at": "2026-06-29T12:06:00+08:00", - "status": "blocked_required_sources_missing", + "status": "ready_for_template_copy_apply_gate", "priority": "P0-004", "scope": "dev_prod_cicd_baseline", "readback": { "workplan_id": "P0-004", "workplan_title": "補 dev / prod CI/CD baseline", - "scorecard_completion_percent": 40, + "scorecard_completion_percent": 60, "baseline_matrix_present": true, "dev_missing_count": 10, "prod_cicd_gap_count": 10, "github_mirror_status": "removed_deleted_do_not_use", - "safe_next_step": "restore_or_recreate_tracked_warning_step_source_before_workflow_enablement" + "safe_next_step": "open_template_copy_apply_gate_after_source_readiness_green" }, "required_sources": [ { @@ -83,30 +83,20 @@ } ], "blockers": [ - "tracked_warning_step_source_files_missing", - "workflow_enablement_blocked_until_source_readiness_green" + "template_copy_apply_gate_required_before_workflow_enablement" ], "next_actions": [ - "restore_or_recreate_warning_step_owner_package_preflight_plan_apply_gate_dashboard_sources", - "add_focused_tests_for_recreated_sources_before_any_workflow_copy", - "keep_workflow_modification_allowed_false_until_source_readiness_green" + "run_focused_tests_for_recreated_sources", + "open_template_copy_apply_gate_before_any_workflow_copy", + "keep_workflow_modification_allowed_false_until_apply_gate" ], "rollups": { "required_source_count": 11, - "present_required_source_count": 3, - "missing_required_source_count": 8, - "source_readiness_percent": 27, - "blocked_source_ids": [ - "warning_step_owner_package_service", - "warning_step_owner_response_preflight_service", - "warning_step_template_copy_execution_plan_service", - "warning_step_template_copy_apply_gate_service", - "warning_step_dashboard_service", - "product_onboarding_guard_service", - "onboarding_reminder_contract_service", - "new_product_onboarding_page_model_service" - ], - "hard_blocker_count": 2, + "present_required_source_count": 11, + "missing_required_source_count": 0, + "source_readiness_percent": 100, + "blocked_source_ids": [], + "hard_blocker_count": 1, "next_action_count": 3 }, "operation_boundaries": {