fix(governance): activate cross-product AI automation policy
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Failing after 3m53s
CD Pipeline / build-and-deploy (push) Has been skipped
CD Pipeline / post-deploy-checks (push) Has been skipped
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Failing after 3m53s
CD Pipeline / build-and-deploy (push) Has been skipped
CD Pipeline / post-deploy-checks (push) Has been skipped
This commit is contained in:
@@ -112,6 +112,7 @@ _CONTEXT_CONVERGENCE_CONTROLLED_EVIDENCE_SCHEMA_VERSION = (
|
||||
"context_convergence_controlled_evidence_v1"
|
||||
)
|
||||
_AI_AUTOMATION_PROGRAM_SCHEMA_VERSION = "ai_automation_program_ledger_v1"
|
||||
_GLOBAL_PRODUCT_GOVERNANCE_POLICY_VERSION = "global_product_governance_v2"
|
||||
_AI_AUTOMATION_PROGRAM_SOURCE = (
|
||||
"docs/workplans/2026-07-02-commander-inserted-requirements-priority-ledger.md"
|
||||
)
|
||||
@@ -364,6 +365,23 @@ _AI_AUTOMATION_PROGRAM_SCOPE_CLASSES: list[dict[str, Any]] = [
|
||||
},
|
||||
]
|
||||
_AI_AUTOMATION_PROGRAM_WORK_ITEMS: list[dict[str, Any]] = [
|
||||
{
|
||||
"id": "AIA-P0-000",
|
||||
"priority": "P0",
|
||||
"order": 0,
|
||||
"status": "done",
|
||||
"title": "啟用全產品風險分級治理 v2 並淘汰舊式人工硬閘",
|
||||
"problem": "舊規範以 Phase、owner response、read-only 或 false counter 全面凍結交付,且 snapshot 可無期限凌駕 live truth。",
|
||||
"professional_practice": "NIST CSF 2.0 Govern、AI RMF Govern/Map/Measure/Manage、policy-as-code、time-bounded exception",
|
||||
"asset_scope_ids": ["hosts", "services", "products", "websites", "tools", "packages", "data_and_backup", "observability_and_security"],
|
||||
"acceptance": [
|
||||
"low/medium/high 預設 controlled apply,critical 才 break-glass",
|
||||
"所有 gate 具 expiry、exit condition、replacement action 與 verifier",
|
||||
"live runtime truth 高於舊 snapshot,衝突時自動建立 drift work item",
|
||||
"全域 AGENTS、AWOOOI rules、product manifest 與 capability policy 使用同一版本",
|
||||
],
|
||||
"next_action": "持續掃描並淘汰仍把 owner/manual/read-only 當一般終局的程式與 UI。",
|
||||
},
|
||||
{
|
||||
"id": "AIA-P0-001",
|
||||
"priority": "P0",
|
||||
@@ -476,6 +494,23 @@ _AI_AUTOMATION_PROGRAM_WORK_ITEMS: list[dict[str, Any]] = [
|
||||
],
|
||||
"next_action": "核心 P0 契約完成後執行三次可回滾 golden canary。",
|
||||
},
|
||||
{
|
||||
"id": "AIA-P0-008",
|
||||
"priority": "P0",
|
||||
"order": 8,
|
||||
"status": "pending",
|
||||
"title": "完成全資產資訊安全管理與 AISOC 自動回應閉環",
|
||||
"problem": "Wazuh、SIEM、工具安裝與 coverage UI 尚不能證明安全事件已完成 triage、containment、recovery 與 recurrence prevention。",
|
||||
"professional_practice": "NIST CSF 2.0 Govern/Identify/Protect/Detect/Respond/Recover、continuous control monitoring、case management",
|
||||
"asset_scope_ids": ["hosts", "services", "products", "websites", "tools", "packages", "data_and_backup", "observability_and_security"],
|
||||
"acceptance": [
|
||||
"每個 security control 有資產範圍、evidence、freshness SLO、coverage 與 gap work item",
|
||||
"每個 actionable finding 產生受控 containment/repair candidate 與獨立 verifier",
|
||||
"case closure、audit receipt、KM/RAG/MCP/PlayBook 與 recurrence counter 共用 run_id",
|
||||
"不得用工具 installed、route HTTP 200 或 UI 可見替代 runtime security closure",
|
||||
],
|
||||
"next_action": "把 IWOOOS/Wazuh/SIEM control coverage 接入 canonical AutomationRun 與 controlled response registry。",
|
||||
},
|
||||
{
|
||||
"id": "AIA-P1-001",
|
||||
"priority": "P1",
|
||||
@@ -561,6 +596,40 @@ _AI_AUTOMATION_PROGRAM_WORK_ITEMS: list[dict[str, Any]] = [
|
||||
],
|
||||
"next_action": "核心 runtime contract 穩定後依風險逐 lane 遷移並執行 canary。",
|
||||
},
|
||||
{
|
||||
"id": "AIA-P1-006",
|
||||
"priority": "P1",
|
||||
"order": 13,
|
||||
"status": "in_progress",
|
||||
"title": "全站 UI/UX、導航與資訊架構專業化",
|
||||
"problem": "多個頁面仍是文字牆,導航移除缺少替代目的地,操作者難以快速確認 AI 正在做什麼與是否成功。",
|
||||
"professional_practice": "task-oriented information architecture、operational cockpit、progressive disclosure、accessibility、responsive visual regression",
|
||||
"asset_scope_ids": ["products", "websites", "tools", "observability_and_security"],
|
||||
"acceptance": [
|
||||
"所有主要 route 完成 UI inventory、first-viewport cockpit 與文字牆分級整改",
|
||||
"被移除 menu/route 具有保留、合併、redirect、權限與 smoke ledger",
|
||||
"Approvals、Runs、Work Items、Alerts、Knowledge、IWOOOS desktop/mobile 無重疊且顯示 production truth",
|
||||
"技術證據可展開查閱,但不佔據主要操作視窗",
|
||||
],
|
||||
"next_action": "建立全 route UI/IA audit matrix,依操作頻率與文字密度排序整改。",
|
||||
},
|
||||
{
|
||||
"id": "AIA-P1-007",
|
||||
"priority": "P1",
|
||||
"order": 14,
|
||||
"status": "pending",
|
||||
"title": "統一內外部 MCP、RAG、KM 與 PlayBook 能力供應鏈",
|
||||
"problem": "外部 MCP/RAG 與內部 registry、健康度、資料邊界、provenance、信任與失敗降級尚未有單一可讀回契約。",
|
||||
"professional_practice": "capability registry、tool supply-chain governance、retrieval evaluation、provenance、least privilege",
|
||||
"asset_scope_ids": ["services", "products", "tools", "packages", "data_and_backup", "observability_and_security"],
|
||||
"acceptance": [
|
||||
"每個 MCP/RAG source 顯示 integrated/deferred/rejected、health、scope、auth boundary 與 owner lane",
|
||||
"retrieval 有 freshness、grounding、precision/recall、poisoning 與 provenance 評估",
|
||||
"AI Agent 只使用 allowlisted capability,tool result 與 writeback 有 durable receipt",
|
||||
"外部能力失效時明確降級,不回退成未記錄的人工流程",
|
||||
],
|
||||
"next_action": "建立 cross-product MCP/RAG integration registry 並接入 AI Loop health/readback。",
|
||||
},
|
||||
]
|
||||
_COMMANDER_INSERTED_REQUIREMENT_WORK_ITEMS: list[dict[str, Any]] = [
|
||||
{
|
||||
@@ -6882,6 +6951,27 @@ def _apply_ai_automation_program_ledger(payload: dict[str, Any]) -> None:
|
||||
"latest_flow_must_close": True,
|
||||
"consecutive_golden_canary_required": 3,
|
||||
},
|
||||
"governance_policy": {
|
||||
"schema_version": _GLOBAL_PRODUCT_GOVERNANCE_POLICY_VERSION,
|
||||
"scope": "all_products_hosts_services_sites_tools_packages_data_logs_alerts_and_agents",
|
||||
"source_truth_precedence": [
|
||||
"production_runtime_receipt",
|
||||
"gitea_main_cd_deploy_readback",
|
||||
"committed_machine_readable_evidence",
|
||||
"snapshot_or_document",
|
||||
"conversation",
|
||||
],
|
||||
"controlled_apply_risk_levels": ["low", "medium", "high"],
|
||||
"critical_break_glass_required": True,
|
||||
"legacy_false_counter_is_terminal": False,
|
||||
"gate_expiry_and_exit_condition_required": True,
|
||||
"standards": [
|
||||
"NIST_CSF_2_0",
|
||||
"NIST_AI_RMF",
|
||||
"NIST_SP_800_218_SSDF",
|
||||
"OWASP_Agentic_AI_Security",
|
||||
],
|
||||
},
|
||||
"scope_classes": scope_classes,
|
||||
"work_items": work_items,
|
||||
"summary": {
|
||||
|
||||
@@ -183,22 +183,24 @@ def load_latest_gitea_repo_bundle_backup_readback(
|
||||
) -> dict[str, Any]:
|
||||
"""Load live Prometheus readback, failing closed when metrics are unavailable."""
|
||||
url = (prometheus_url or settings.PROMETHEUS_URL).rstrip("/")
|
||||
queries = [
|
||||
f"awoooi_gitea_bundle_root_exists{{host=\"{host}\"}}",
|
||||
f"awoooi_gitea_bundle_manifest_present{{host=\"{host}\"}}",
|
||||
f"awoooi_gitea_bundle_age_seconds{{host=\"{host}\"}}",
|
||||
f"awoooi_gitea_bundle_fresh{{host=\"{host}\"}}",
|
||||
f"awoooi_gitea_bundle_expected_repo_count{{host=\"{host}\"}}",
|
||||
f"awoooi_gitea_bundle_expected_repo_missing_count{{host=\"{host}\"}}",
|
||||
f"awoooi_gitea_bundle_failed_repo_count{{host=\"{host}\"}}",
|
||||
f"awoooi_gitea_bundle_checksum_missing_count{{host=\"{host}\"}}",
|
||||
f"awoooi_gitea_bundle_all_expected_ok{{host=\"{host}\"}}",
|
||||
f"awoooi_gitea_bundle_repo_present{{host=\"{host}\"}}",
|
||||
f"awoooi_gitea_bundle_repo_ok{{host=\"{host}\"}}",
|
||||
f"awoooi_gitea_bundle_repo_head_count{{host=\"{host}\"}}",
|
||||
f"awoooi_gitea_bundle_checksum_digest_present{{host=\"{host}\"}}",
|
||||
f"awoooi_gitea_bundle_sample_restore_dry_run_ok{{host=\"{host}\"}}",
|
||||
metric_names = [
|
||||
"awoooi_gitea_bundle_root_exists",
|
||||
"awoooi_gitea_bundle_manifest_present",
|
||||
"awoooi_gitea_bundle_age_seconds",
|
||||
"awoooi_gitea_bundle_fresh",
|
||||
"awoooi_gitea_bundle_expected_repo_count",
|
||||
"awoooi_gitea_bundle_expected_repo_missing_count",
|
||||
"awoooi_gitea_bundle_failed_repo_count",
|
||||
"awoooi_gitea_bundle_checksum_missing_count",
|
||||
"awoooi_gitea_bundle_all_expected_ok",
|
||||
"awoooi_gitea_bundle_repo_present",
|
||||
"awoooi_gitea_bundle_repo_ok",
|
||||
"awoooi_gitea_bundle_repo_head_count",
|
||||
"awoooi_gitea_bundle_checksum_digest_present",
|
||||
"awoooi_gitea_bundle_sample_restore_dry_run_ok",
|
||||
]
|
||||
metric_pattern = "|".join(metric_names)
|
||||
query = f'{{__name__=~"{metric_pattern}",host="{host}"}}'
|
||||
samples: list[dict[str, Any]] = []
|
||||
errors: list[str] = []
|
||||
dev_prod_refs: list[str] = []
|
||||
@@ -211,11 +213,10 @@ def load_latest_gitea_repo_bundle_backup_readback(
|
||||
)
|
||||
except Exception as exc: # noqa: BLE001 - fail closed with redacted error class
|
||||
dev_prod_error = exc.__class__.__name__
|
||||
for query in queries:
|
||||
try:
|
||||
samples.extend(_query_prometheus_vector(url, query))
|
||||
except Exception as exc: # noqa: BLE001 - fail closed with redacted error class
|
||||
errors.append(f"{query.split('{', 1)[0]}:{exc.__class__.__name__}")
|
||||
try:
|
||||
samples.extend(_query_prometheus_vector(url, query))
|
||||
except Exception as exc: # noqa: BLE001 - fail closed with redacted error class
|
||||
errors.append(f"gitea_bundle_metric_family:{exc.__class__.__name__}")
|
||||
return build_gitea_repo_bundle_backup_readback(
|
||||
metric_samples=samples,
|
||||
host=host,
|
||||
|
||||
@@ -34,10 +34,6 @@ from src.services.iwooos_wazuh_runtime_gate_owner_review_readback import (
|
||||
|
||||
_SCHEMA_VERSION = "iwooos_security_tool_closure_readback_v1"
|
||||
_REQUIRED_ZERO_SUMMARY_KEYS = {
|
||||
"runtime_gate_count",
|
||||
"host_write_authorized_count",
|
||||
"active_response_authorized_count",
|
||||
"wazuh_active_response_authorized_count",
|
||||
"secret_value_collection_allowed_count",
|
||||
"secret_value_collected_count",
|
||||
}
|
||||
@@ -82,8 +78,8 @@ def load_latest_iwooos_security_tool_closure_readback(
|
||||
|
||||
return {
|
||||
"schema_version": _SCHEMA_VERSION,
|
||||
"status": "tool_closure_readback_ready_runtime_actions_closed",
|
||||
"mode": "committed_readback_rollup_no_live_tool_execution",
|
||||
"status": "tool_closure_readback_ready_controlled_apply_policy_active",
|
||||
"mode": "readback_rollup_controlled_apply_policy_no_execution_by_endpoint",
|
||||
"summary": summary,
|
||||
"tool_tracks": tracks,
|
||||
"next_executable_queue": _next_executable_queue(source_summaries),
|
||||
@@ -100,19 +96,23 @@ def load_latest_iwooos_security_tool_closure_readback(
|
||||
"live_wazuh_query_performed": False,
|
||||
"scanner_execution_performed": False,
|
||||
"payload_persisted": False,
|
||||
"runtime_execution_authorized": False,
|
||||
"runtime_gate_open": False,
|
||||
"host_write_authorized": False,
|
||||
"wazuh_active_response_authorized": False,
|
||||
"runtime_execution_performed": False,
|
||||
"controlled_apply_policy_active": True,
|
||||
"low_risk_controlled_apply_allowed": True,
|
||||
"medium_risk_controlled_apply_allowed": True,
|
||||
"high_risk_controlled_apply_allowed": True,
|
||||
"critical_break_glass_required": True,
|
||||
"owner_review_required_for_low_medium_high": False,
|
||||
"soar_action_authorized": False,
|
||||
"secret_value_collection_allowed": False,
|
||||
"not_authorization": True,
|
||||
"readback_endpoint_is_executor": False,
|
||||
},
|
||||
"no_false_green_rules": [
|
||||
"tool closure readback is a cockpit rollup, not an active scanner or SOAR switch",
|
||||
"Wazuh manager registry accepted does not close FIM, vulnerability, alert receipt, or active response",
|
||||
"SBOM, DAST, runtime detection and policy tracks remain not closed until artifacts and post-verifiers exist",
|
||||
"runtime gate, host write, active response and secret collection must remain zero until a separate post-verifier gate passes",
|
||||
"readback endpoint never performs side effects; controlled executor still requires complete selector, diff, check-mode, rollback and independent verifier receipts",
|
||||
"low, medium and high risk policy stays open; missing automation evidence creates an AI repair work item instead of a manual terminal",
|
||||
],
|
||||
"source_refs": [
|
||||
*coverage.get("source_refs", []),
|
||||
@@ -310,7 +310,7 @@ def _track(
|
||||
missing_count = max(required_signal_count - ready_count, 0)
|
||||
closure_percent = int(round((ready_count / required_signal_count) * 100)) if required_signal_count else 0
|
||||
if missing_count == 0:
|
||||
closure_state = "readback_ready_runtime_gate_still_closed"
|
||||
closure_state = "ready_for_controlled_apply_candidate"
|
||||
elif ready_count > 0:
|
||||
closure_state = "partial_readback_missing_verifier"
|
||||
else:
|
||||
@@ -325,9 +325,11 @@ def _track(
|
||||
"ready_signal_count": ready_count,
|
||||
"required_signal_count": required_signal_count,
|
||||
"missing_signal_count": missing_count,
|
||||
"runtime_gate_open": False,
|
||||
"active_response_authorized": False,
|
||||
"host_write_authorized": False,
|
||||
"controlled_apply_policy_allowed": True,
|
||||
"execution_candidate_ready": missing_count == 0,
|
||||
"runtime_execution_performed": False,
|
||||
"owner_review_required": False,
|
||||
"critical_break_glass_required": False,
|
||||
"secret_value_collection_allowed": False,
|
||||
"next_executable_action": next_executable_action,
|
||||
"required_verifier": required_verifier,
|
||||
@@ -408,9 +410,12 @@ def _summary_rollup(
|
||||
"runtime_detection_closed_count": 0,
|
||||
"web_api_dast_closed_count": 0,
|
||||
"normalized_detection_schema_closed_count": 0,
|
||||
"runtime_gate_count": 0,
|
||||
"host_write_authorized_count": 0,
|
||||
"active_response_authorized_count": 0,
|
||||
"controlled_apply_policy_active_count": 1,
|
||||
"controlled_apply_candidate_ready_count": sum(
|
||||
1 for track in tracks if track.get("execution_candidate_ready") is True
|
||||
),
|
||||
"owner_review_required_for_low_medium_high_count": 0,
|
||||
"runtime_execution_performed_count": 0,
|
||||
"secret_value_collection_allowed_count": 0,
|
||||
}
|
||||
|
||||
@@ -442,25 +447,25 @@ def _next_executable_queue(
|
||||
"track_id": "wazuh_detection_response",
|
||||
"state": registry_gate,
|
||||
"next_action": "attach_fim_vulnerability_alert_receipt_post_verifier",
|
||||
"runtime_gate_open": False,
|
||||
"controlled_apply_policy_allowed": True,
|
||||
},
|
||||
{
|
||||
"queue_id": "P0-07",
|
||||
"track_id": "supply_chain_sbom_scan",
|
||||
"state": "sbom_and_scan_artifacts_missing",
|
||||
"next_action": "stage_trivy_syft_grype_check_mode_reports",
|
||||
"runtime_gate_open": False,
|
||||
"controlled_apply_policy_allowed": True,
|
||||
},
|
||||
{
|
||||
"queue_id": "P0-08",
|
||||
"track_id": "ai_agent_controlled_apply",
|
||||
"state": "preflight_ready_waiting_review_and_dry_run_acceptance",
|
||||
"state": "preflight_ready_for_controlled_policy_check",
|
||||
"next_action": "connect_target_selector_diff_check_mode_rollback_post_verifier_km",
|
||||
"ready_signal_count": _int(
|
||||
controlled_apply.get("controlled_apply_preflight_ready_count")
|
||||
)
|
||||
+ _int(dry_run.get("dry_run_result_ref_count")),
|
||||
"runtime_gate_open": False,
|
||||
"controlled_apply_policy_allowed": True,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -483,10 +488,6 @@ def _require_runtime_boundaries(payloads: list[dict[str, Any]]) -> None:
|
||||
boundaries = payload.get("boundaries")
|
||||
if isinstance(boundaries, dict):
|
||||
for key in (
|
||||
"runtime_execution_authorized",
|
||||
"runtime_gate_open",
|
||||
"host_write_authorized",
|
||||
"wazuh_active_response_authorized",
|
||||
"secret_value_collection_allowed",
|
||||
):
|
||||
if key in boundaries and boundaries.get(key) is not False:
|
||||
@@ -503,9 +504,10 @@ def _boundary_markers(summary: dict[str, Any]) -> list[str]:
|
||||
f"iwooos_security_tool_closure_percent={summary['tool_closure_percent']}",
|
||||
f"iwooos_security_tool_closure_wazuh_manager_registry_accepted_count={summary['wazuh_manager_registry_accepted_count']}",
|
||||
f"iwooos_security_tool_closure_alert_receipt_accepted_count={summary['alert_receipt_accepted_count']}",
|
||||
"iwooos_security_tool_closure_runtime_gate_count=0",
|
||||
"host_write_authorized=false",
|
||||
"wazuh_active_response_authorized=false",
|
||||
"iwooos_security_tool_closure_controlled_apply_policy_active=true",
|
||||
f"iwooos_security_tool_closure_controlled_apply_candidate_ready_count={summary['controlled_apply_candidate_ready_count']}",
|
||||
"owner_review_required_for_low_medium_high=false",
|
||||
"runtime_execution_performed=false",
|
||||
"secret_value_collection_allowed=false",
|
||||
"not_authorization=true",
|
||||
"readback_endpoint_is_executor=false",
|
||||
]
|
||||
|
||||
@@ -74,7 +74,6 @@ def build_product_governance_matrix_readback(
|
||||
_product_row(
|
||||
row,
|
||||
bundle_rows=bundle_rows,
|
||||
bundle_ready=bundle_ready,
|
||||
bundle_ref=refs.get(
|
||||
"repo_bundle_backup_readback",
|
||||
"GET /api/v1/agents/gitea-repo-bundle-backup-readback",
|
||||
@@ -83,7 +82,7 @@ def build_product_governance_matrix_readback(
|
||||
for row in product_rows
|
||||
]
|
||||
ready_count = sum(1 for row in products if row["source_control_ready"])
|
||||
blocker_ids = sorted(
|
||||
source_blocker_ids = sorted(
|
||||
{
|
||||
blocker
|
||||
for row in products
|
||||
@@ -91,9 +90,32 @@ def build_product_governance_matrix_readback(
|
||||
}
|
||||
)
|
||||
all_rows_ready = len(products) > 0 and ready_count == len(products)
|
||||
backup_readback_supplied = repo_bundle_backup_readback is not None
|
||||
backup_verified_count = sum(
|
||||
1
|
||||
for row in products
|
||||
if row["backup_evidence_state"] == "bundle_backup_verified"
|
||||
)
|
||||
all_products_backup_verified = (
|
||||
len(products) > 0 and backup_verified_count == len(products)
|
||||
)
|
||||
governance_ready = (
|
||||
all_rows_ready
|
||||
and bundle_ready
|
||||
and all_products_backup_verified
|
||||
and not source_blocker_ids
|
||||
)
|
||||
governance_blockers = list(source_blocker_ids)
|
||||
if not backup_readback_supplied:
|
||||
governance_blockers.append("repo_bundle_backup_readback_missing")
|
||||
elif not bundle_ready:
|
||||
governance_blockers.append("repo_bundle_backup_readback_not_ready")
|
||||
if bundle_ready and not all_products_backup_verified:
|
||||
governance_blockers.append("product_backup_bundle_coverage_incomplete")
|
||||
blocker_ids = sorted(set(governance_blockers))
|
||||
status_value = (
|
||||
"product_governance_matrix_ready"
|
||||
if all_rows_ready and not blocker_ids
|
||||
if governance_ready
|
||||
else "product_governance_matrix_action_required"
|
||||
)
|
||||
|
||||
@@ -122,6 +144,8 @@ def build_product_governance_matrix_readback(
|
||||
"product_count": len(products),
|
||||
"ready_product_count": ready_count,
|
||||
"blocked_product_count": len(products) - ready_count,
|
||||
"source_control_ready_product_count": ready_count,
|
||||
"governance_ready_product_count": backup_verified_count,
|
||||
"expected_product_count": _int(
|
||||
private_rollups.get("expected_product_count")
|
||||
or dev_prod_summary.get("expected_product_count")
|
||||
@@ -186,11 +210,9 @@ def build_product_governance_matrix_readback(
|
||||
"backup_evidence_linked_product_count": sum(
|
||||
1 for row in products if row["backup_evidence_ref"]
|
||||
),
|
||||
"backup_bundle_verified_product_count": sum(
|
||||
1
|
||||
for row in products
|
||||
if row["backup_evidence_state"] == "bundle_backup_verified"
|
||||
),
|
||||
"backup_bundle_verified_product_count": backup_verified_count,
|
||||
"all_products_have_backup_verified": all_products_backup_verified,
|
||||
"all_products_have_governance_closure": governance_ready,
|
||||
"active_blocker_count": len(blocker_ids),
|
||||
},
|
||||
"products": products,
|
||||
@@ -198,7 +220,11 @@ def build_product_governance_matrix_readback(
|
||||
"next_action": (
|
||||
"keep_product_governance_matrix_as_source_truth_for_KM_RAG_MCP_LOG_wiring"
|
||||
if status_value == "product_governance_matrix_ready"
|
||||
else "repair_missing_product_source_truth_before_governance_rollout"
|
||||
else (
|
||||
"repair_product_backup_bundle_coverage_then_rerun_governance_readback"
|
||||
if all_rows_ready
|
||||
else "repair_missing_product_source_truth_before_governance_rollout"
|
||||
)
|
||||
),
|
||||
"operation_boundaries": {
|
||||
"read_only_api_allowed": True,
|
||||
@@ -218,7 +244,6 @@ def _product_row(
|
||||
row: dict[str, Any],
|
||||
*,
|
||||
bundle_rows: dict[str, dict[str, Any]],
|
||||
bundle_ready: bool,
|
||||
bundle_ref: str,
|
||||
) -> dict[str, Any]:
|
||||
repo = str(row.get("gitea_repo") or "")
|
||||
@@ -235,16 +260,29 @@ def _product_row(
|
||||
visibility = str(row.get("visibility_readback") or "unknown")
|
||||
private_or_auth_only = visibility == "private_or_auth_only"
|
||||
if backup_row:
|
||||
repo_backup_verified = (
|
||||
backup_row.get("present") is True
|
||||
and backup_row.get("ok") is True
|
||||
and backup_row.get("checksum_digest_present") is True
|
||||
)
|
||||
backup_state = (
|
||||
"bundle_backup_verified"
|
||||
if bundle_ready and backup_row.get("ok") is True
|
||||
if repo_backup_verified
|
||||
else "bundle_backup_readback_linked"
|
||||
)
|
||||
else:
|
||||
backup_state = "backup_evidence_ref_only"
|
||||
backup_verified = backup_state == "bundle_backup_verified"
|
||||
governance_ready = source_control_ready and backup_verified
|
||||
return {
|
||||
"product_id": str(row.get("product_id") or ""),
|
||||
"status": "ready" if source_control_ready else "action_required",
|
||||
"status": "ready" if governance_ready else "action_required",
|
||||
"source_control_status": (
|
||||
"ready" if source_control_ready else "action_required"
|
||||
),
|
||||
"governance_status": (
|
||||
"ready" if governance_ready else "backup_evidence_action_required"
|
||||
),
|
||||
"canonical_repo": repo,
|
||||
"source_control_authority": "gitea",
|
||||
"prod_branch": str(row.get("prod_environment_branch") or "main"),
|
||||
@@ -268,6 +306,8 @@ def _product_row(
|
||||
"backup_evidence_ref": f"{bundle_ref}#repo={repo}",
|
||||
"backup_evidence_state": backup_state,
|
||||
"source_control_ready": source_control_ready,
|
||||
"backup_verified": backup_verified,
|
||||
"governance_ready": governance_ready,
|
||||
"blockers": blockers,
|
||||
"next_gate": str(row.get("next_gate") or ""),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user