feat(awooop): surface owner release closure tasks
This commit is contained in:
@@ -47,6 +47,12 @@ from src.services.awooop_truth_chain_service import (
|
||||
_summarize_mcp,
|
||||
fetch_truth_chain,
|
||||
)
|
||||
from src.services.ai_agent_result_capture_owner_release_approval_gate import (
|
||||
load_latest_ai_agent_result_capture_owner_release_approval_gate,
|
||||
)
|
||||
from src.services.ai_agent_result_capture_release_verifier_preflight_gate import (
|
||||
load_latest_ai_agent_result_capture_release_verifier_preflight_gate,
|
||||
)
|
||||
from src.services.governance_km_stale_review_service import (
|
||||
query_km_stale_owner_review_completion_queue,
|
||||
)
|
||||
@@ -4075,6 +4081,217 @@ def _status_chain_ansible_dry_run_only(
|
||||
)
|
||||
|
||||
|
||||
def _compact_release_items(
|
||||
items: Any,
|
||||
*,
|
||||
id_field: str,
|
||||
summary_field: str,
|
||||
limit: int = 5,
|
||||
) -> list[dict[str, Any]]:
|
||||
if not isinstance(items, list):
|
||||
return []
|
||||
compacted: list[dict[str, Any]] = []
|
||||
for item in items:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
compacted.append(
|
||||
{
|
||||
"id": item.get(id_field),
|
||||
"display_name": item.get("display_name"),
|
||||
"owner_agent": item.get("owner_agent"),
|
||||
"status": item.get("status"),
|
||||
"summary": item.get(summary_field),
|
||||
"runtime_write_allowed": item.get("runtime_write_allowed", False),
|
||||
}
|
||||
)
|
||||
if len(compacted) >= limit:
|
||||
break
|
||||
return compacted
|
||||
|
||||
|
||||
def _owner_release_package_bridge() -> dict[str, Any]:
|
||||
try:
|
||||
payload = load_latest_ai_agent_result_capture_owner_release_approval_gate()
|
||||
except Exception as exc: # pragma: no cover - fail-closed read model guard
|
||||
return {
|
||||
"schema_version": "awooop_owner_release_package_bridge_v1",
|
||||
"status": "snapshot_unavailable",
|
||||
"source_schema_version": None,
|
||||
"source_task_id": None,
|
||||
"runtime_authority": "read_model_unavailable_no_write",
|
||||
"packet_count": 0,
|
||||
"approval_required_count": 0,
|
||||
"blocked_count": 0,
|
||||
"owner_release_approved_count": 0,
|
||||
"maintenance_window_approved_count": 0,
|
||||
"rollback_owner_confirmed_count": 0,
|
||||
"operator_action_count": 0,
|
||||
"packets": [],
|
||||
"operator_actions": [],
|
||||
"error": str(exc)[:160],
|
||||
}
|
||||
|
||||
rollups = payload.get("rollups") if isinstance(payload.get("rollups"), dict) else {}
|
||||
status = (
|
||||
payload.get("program_status")
|
||||
if isinstance(payload.get("program_status"), dict)
|
||||
else {}
|
||||
)
|
||||
return {
|
||||
"schema_version": "awooop_owner_release_package_bridge_v1",
|
||||
"status": "owner_review_required",
|
||||
"source_schema_version": payload.get("schema_version"),
|
||||
"source_task_id": status.get("current_task_id"),
|
||||
"runtime_authority": status.get("runtime_authority"),
|
||||
"packet_count": _safe_int(rollups.get("owner_release_approval_packet_count")),
|
||||
"approval_required_count": _safe_int(rollups.get("approval_required_packet_count")),
|
||||
"blocked_count": _safe_int(rollups.get("blocked_packet_count")),
|
||||
"owner_release_approved_count": _safe_int(rollups.get("owner_release_approved_count")),
|
||||
"maintenance_window_approved_count": _safe_int(rollups.get("maintenance_window_approved_count")),
|
||||
"rollback_owner_confirmed_count": _safe_int(rollups.get("rollback_owner_confirmed_count")),
|
||||
"operator_action_count": _safe_int(rollups.get("operator_action_count")),
|
||||
"packets": _compact_release_items(
|
||||
payload.get("owner_release_approval_packets"),
|
||||
id_field="packet_id",
|
||||
summary_field="approval_summary",
|
||||
),
|
||||
"operator_actions": _compact_release_items(
|
||||
payload.get("operator_actions"),
|
||||
id_field="action_id",
|
||||
summary_field="operator_instruction",
|
||||
limit=3,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def _release_verifier_package_bridge() -> dict[str, Any]:
|
||||
try:
|
||||
payload = load_latest_ai_agent_result_capture_release_verifier_preflight_gate()
|
||||
except Exception as exc: # pragma: no cover - fail-closed read model guard
|
||||
return {
|
||||
"schema_version": "awooop_release_verifier_package_bridge_v1",
|
||||
"status": "snapshot_unavailable",
|
||||
"source_schema_version": None,
|
||||
"source_task_id": None,
|
||||
"runtime_authority": "read_model_unavailable_no_write",
|
||||
"verifier_count": 0,
|
||||
"rollback_count": 0,
|
||||
"maintenance_hold_count": 0,
|
||||
"live_apply_hold_count": 0,
|
||||
"owner_release_authorized_count": 0,
|
||||
"post_release_verifier_ready_count": 0,
|
||||
"live_apply_release_pass_count": 0,
|
||||
"operator_action_count": 0,
|
||||
"verifier_gates": [],
|
||||
"operator_actions": [],
|
||||
"error": str(exc)[:160],
|
||||
}
|
||||
|
||||
rollups = payload.get("rollups") if isinstance(payload.get("rollups"), dict) else {}
|
||||
status = (
|
||||
payload.get("program_status")
|
||||
if isinstance(payload.get("program_status"), dict)
|
||||
else {}
|
||||
)
|
||||
return {
|
||||
"schema_version": "awooop_release_verifier_package_bridge_v1",
|
||||
"status": "preflight_review_required",
|
||||
"source_schema_version": payload.get("schema_version"),
|
||||
"source_task_id": status.get("current_task_id"),
|
||||
"runtime_authority": status.get("runtime_authority"),
|
||||
"verifier_count": _safe_int(rollups.get("release_verifier_preflight_count")),
|
||||
"rollback_count": _safe_int(rollups.get("rollback_verifier_preflight_count")),
|
||||
"maintenance_hold_count": _safe_int(rollups.get("maintenance_window_verifier_hold_count")),
|
||||
"live_apply_hold_count": _safe_int(rollups.get("live_apply_verifier_hold_count")),
|
||||
"owner_release_authorized_count": _safe_int(rollups.get("owner_release_authorized_count")),
|
||||
"post_release_verifier_ready_count": _safe_int(rollups.get("post_release_verifier_ready_count")),
|
||||
"live_apply_release_pass_count": _safe_int(rollups.get("live_apply_release_pass_count")),
|
||||
"operator_action_count": _safe_int(rollups.get("operator_action_count")),
|
||||
"verifier_gates": _compact_release_items(
|
||||
payload.get("release_verifier_preflight_gates"),
|
||||
id_field="readback_id",
|
||||
summary_field="readback_summary",
|
||||
),
|
||||
"operator_actions": _compact_release_items(
|
||||
payload.get("operator_actions"),
|
||||
id_field="action_id",
|
||||
summary_field="operator_instruction",
|
||||
limit=3,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def _apply_gate_closure_tasks(
|
||||
*,
|
||||
source_ref: str,
|
||||
safe_source_ref: str,
|
||||
catalog_id: str,
|
||||
owner_release_package: dict[str, Any],
|
||||
verifier_package: dict[str, Any],
|
||||
) -> list[dict[str, Any]]:
|
||||
return [
|
||||
{
|
||||
"key": "owner_release_packet_review",
|
||||
"status": "ready_for_owner_review",
|
||||
"owner_agent": "openclaw",
|
||||
"source_asset_id": "agent-result-capture-owner-release-approval-gate:P2-131",
|
||||
"work_item_id": f"owner-release-review:awoooi:{safe_source_ref}",
|
||||
"summary": (
|
||||
f"packets={_safe_int(owner_release_package.get('packet_count'))}; "
|
||||
f"approved={_safe_int(owner_release_package.get('owner_release_approved_count'))}"
|
||||
),
|
||||
"next_step": "review_owner_release_packet_and_evidence_refs",
|
||||
"runtime_write_allowed": False,
|
||||
},
|
||||
{
|
||||
"key": "maintenance_window_rollback_owner",
|
||||
"status": "approval_required",
|
||||
"owner_agent": "hermes",
|
||||
"source_asset_id": "agent-result-capture-owner-release-approval-gate:P2-131",
|
||||
"work_item_id": f"maintenance-rollback-review:awoooi:{safe_source_ref}",
|
||||
"summary": (
|
||||
f"maintenance_approved={_safe_int(owner_release_package.get('maintenance_window_approved_count'))}; "
|
||||
f"rollback_confirmed={_safe_int(owner_release_package.get('rollback_owner_confirmed_count'))}"
|
||||
),
|
||||
"next_step": "collect_maintenance_window_and_rollback_owner",
|
||||
"runtime_write_allowed": False,
|
||||
},
|
||||
{
|
||||
"key": "controlled_execution_authorization",
|
||||
"status": "blocked_before_runtime_gate",
|
||||
"owner_agent": "sre",
|
||||
"source_asset_id": f"ansible-apply-candidate:{catalog_id}",
|
||||
"work_item_id": f"controlled-execution-gate:awoooi:{safe_source_ref}",
|
||||
"summary": f"incident={source_ref}; runtime_gate=0",
|
||||
"next_step": "owner_release_required_before_controlled_apply",
|
||||
"runtime_write_allowed": False,
|
||||
},
|
||||
{
|
||||
"key": "post_apply_verifier_preflight",
|
||||
"status": "preflight_review_required",
|
||||
"owner_agent": "openclaw",
|
||||
"source_asset_id": "agent-result-capture-release-verifier-preflight-gate:P2-136",
|
||||
"work_item_id": f"post-apply-verifier:awoooi:{safe_source_ref}",
|
||||
"summary": (
|
||||
f"verifier={_safe_int(verifier_package.get('verifier_count'))}; "
|
||||
f"ready={_safe_int(verifier_package.get('post_release_verifier_ready_count'))}"
|
||||
),
|
||||
"next_step": "review_post_apply_verifier_before_any_apply",
|
||||
"runtime_write_allowed": False,
|
||||
},
|
||||
{
|
||||
"key": "km_playbook_trust_writeback_plan",
|
||||
"status": "blocked_until_verifier_passes",
|
||||
"owner_agent": "hermes",
|
||||
"source_asset_id": f"playbook-trust-update-candidate:{catalog_id}",
|
||||
"work_item_id": f"km-playbook-writeback:awoooi:{safe_source_ref}",
|
||||
"summary": "km_write=0; playbook_trust_write=0",
|
||||
"next_step": "prepare_km_and_playbook_writeback_after_verified_execution",
|
||||
"runtime_write_allowed": False,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def _status_chain_ansible_apply_gate_handoff(
|
||||
*,
|
||||
ansible_dry_run_only: bool,
|
||||
@@ -4180,6 +4397,15 @@ def _status_chain_ansible_apply_gate_handoff(
|
||||
closure_total_count = len(closure_gates)
|
||||
closure_blocked_count = sum(1 for gate in closure_gates if gate["status"] == "blocked")
|
||||
closure_completion_percent = int(round((closure_ready_count / closure_total_count) * 100))
|
||||
owner_release_package = _owner_release_package_bridge()
|
||||
verifier_package = _release_verifier_package_bridge()
|
||||
closure_tasks = _apply_gate_closure_tasks(
|
||||
source_ref=str(source_ref),
|
||||
safe_source_ref=safe_source_ref,
|
||||
catalog_id=str(catalog_id),
|
||||
owner_release_package=owner_release_package,
|
||||
verifier_package=verifier_package,
|
||||
)
|
||||
|
||||
return {
|
||||
"schema_version": "awooop_automation_handoff_v1",
|
||||
@@ -4244,6 +4470,9 @@ def _status_chain_ansible_apply_gate_handoff(
|
||||
"status": "read_only",
|
||||
},
|
||||
],
|
||||
"owner_release_package": owner_release_package,
|
||||
"release_verifier_package": verifier_package,
|
||||
"closure_tasks": closure_tasks,
|
||||
},
|
||||
"candidate": {
|
||||
"catalog_id": catalog_id,
|
||||
|
||||
@@ -1862,6 +1862,45 @@ def test_awooop_status_chain_does_not_treat_ansible_check_mode_as_repair() -> No
|
||||
assert closure["readback_assets"][0]["asset_id"] == (
|
||||
"agent-result-capture-owner-approved-execution-rehearsal:P2-126"
|
||||
)
|
||||
owner_package = closure["owner_release_package"]
|
||||
assert owner_package["schema_version"] == "awooop_owner_release_package_bridge_v1"
|
||||
assert owner_package["source_schema_version"] == (
|
||||
"ai_agent_result_capture_owner_release_approval_gate_v1"
|
||||
)
|
||||
assert owner_package["source_task_id"] == "P2-131"
|
||||
assert owner_package["packet_count"] == 5
|
||||
assert owner_package["owner_release_approved_count"] == 0
|
||||
assert owner_package["maintenance_window_approved_count"] == 0
|
||||
assert owner_package["rollback_owner_confirmed_count"] == 0
|
||||
assert owner_package["packets"][0]["id"] == "approval_gate_result_capture_writer"
|
||||
verifier_package = closure["release_verifier_package"]
|
||||
assert verifier_package["schema_version"] == (
|
||||
"awooop_release_verifier_package_bridge_v1"
|
||||
)
|
||||
assert verifier_package["source_schema_version"] == (
|
||||
"ai_agent_result_capture_release_verifier_preflight_gate_v1"
|
||||
)
|
||||
assert verifier_package["source_task_id"] == "P2-136"
|
||||
assert verifier_package["verifier_count"] == 5
|
||||
assert verifier_package["post_release_verifier_ready_count"] == 0
|
||||
assert verifier_package["live_apply_release_pass_count"] == 0
|
||||
assert verifier_package["verifier_gates"][0]["id"] == (
|
||||
"release_verifier_preflight_result_capture"
|
||||
)
|
||||
assert [task["key"] for task in closure["closure_tasks"]] == [
|
||||
"owner_release_packet_review",
|
||||
"maintenance_window_rollback_owner",
|
||||
"controlled_execution_authorization",
|
||||
"post_apply_verifier_preflight",
|
||||
"km_playbook_trust_writeback_plan",
|
||||
]
|
||||
assert {task["runtime_write_allowed"] for task in closure["closure_tasks"]} == {False}
|
||||
assert closure["closure_tasks"][0]["source_asset_id"] == (
|
||||
"agent-result-capture-owner-release-approval-gate:P2-131"
|
||||
)
|
||||
assert closure["closure_tasks"][3]["source_asset_id"] == (
|
||||
"agent-result-capture-release-verifier-preflight-gate:P2-136"
|
||||
)
|
||||
assert chain["execution"]["ansible"]["check_mode_total"] == 1
|
||||
assert chain["execution"]["ansible"]["apply_total"] == 0
|
||||
assert chain["execution"]["ansible"]["applied"] is False
|
||||
|
||||
@@ -10180,7 +10180,11 @@
|
||||
"manualInvestigation": "人工調查卡點並補證據",
|
||||
"reviewVerifier": "執行或審查修復後 verifier",
|
||||
"monitorRegression": "持續觀察是否復發",
|
||||
"collectEvidenceOrWait": "補收證據或等待下一筆 recurrence"
|
||||
"collectEvidenceOrWait": "補收證據或等待下一筆 recurrence",
|
||||
"collectMaintenanceRollback": "補齊維護窗口與 rollback owner",
|
||||
"ownerReleaseBeforeApply": "Owner 放行後才可進入受控套用",
|
||||
"reviewPostApplyVerifier": "先審查套用後 verifier,再談執行",
|
||||
"prepareKmPlaybookWriteback": "準備 verifier 通過後的 KM / PlayBook 回寫"
|
||||
},
|
||||
"evidence": {
|
||||
"autoRepair": "Auto-repair",
|
||||
@@ -10217,6 +10221,17 @@
|
||||
"closureBlockedReason": "阻擋:{reason}",
|
||||
"closureOwnerFields": "Owner 放行欄位",
|
||||
"closureReadbackAssets": "只讀回查資產",
|
||||
"ownerReleasePackageTitle": "Owner 放行包",
|
||||
"releaseVerifierPackageTitle": "Verifier 放行前檢",
|
||||
"ownerReleasePacketValue": "packets {packets} · approved {approved}",
|
||||
"maintenanceRollbackValue": "維護 {maintenance} · rollback {rollback}",
|
||||
"operatorActionValue": "operator actions {actions}",
|
||||
"verifierPackageValue": "verifier {verifier} · ready {ready}",
|
||||
"liveApplyHoldValue": "live apply hold {holds} · passed {passed}",
|
||||
"ownerAuthorizedValue": "owner authorized {authorized}",
|
||||
"closureTaskBoardTitle": "閉環任務板",
|
||||
"closureTaskOwner": "Owner:{owner}",
|
||||
"runtimeWrite": "runtime_write={value}",
|
||||
"checklistTitle": "Owner 審查清單",
|
||||
"forbiddenTitle": "禁止動作",
|
||||
"gates": {
|
||||
@@ -10239,10 +10254,25 @@
|
||||
"finalCandidateReadback": "最終候選回查",
|
||||
"releaseVerifierPreflight": "Verifier 放行前檢查"
|
||||
},
|
||||
"closureTasks": {
|
||||
"ownerReleasePacketReview": "審 Owner 放行包",
|
||||
"maintenanceRollbackOwner": "維護 / Rollback",
|
||||
"controlledExecutionAuthorization": "受控執行授權",
|
||||
"postApplyVerifierPreflight": "套用後 Verifier",
|
||||
"kmPlaybookTrustWritebackPlan": "KM / PlayBook 回寫"
|
||||
},
|
||||
"closureStatuses": {
|
||||
"blockedBeforeOwnerRelease": "Owner 放行前受阻",
|
||||
"noWriteRehearsal": "無寫入演練",
|
||||
"readOnly": "只讀"
|
||||
"readOnly": "只讀",
|
||||
"ownerReviewRequired": "需 Owner 審查",
|
||||
"preflightReviewRequired": "需前檢審查",
|
||||
"readyForOwnerReview": "可送 Owner 審查",
|
||||
"approvalRequired": "需批准",
|
||||
"blockedByPolicy": "政策阻擋",
|
||||
"blockedBeforeRuntimeGate": "Runtime gate 前受阻",
|
||||
"blockedUntilVerifierPasses": "Verifier 通過前受阻",
|
||||
"snapshotUnavailable": "快照不可用"
|
||||
},
|
||||
"statuses": {
|
||||
"passed": "已通過",
|
||||
|
||||
@@ -10180,7 +10180,11 @@
|
||||
"manualInvestigation": "人工調查卡點並補證據",
|
||||
"reviewVerifier": "執行或審查修復後 verifier",
|
||||
"monitorRegression": "持續觀察是否復發",
|
||||
"collectEvidenceOrWait": "補收證據或等待下一筆 recurrence"
|
||||
"collectEvidenceOrWait": "補收證據或等待下一筆 recurrence",
|
||||
"collectMaintenanceRollback": "補齊維護窗口與 rollback owner",
|
||||
"ownerReleaseBeforeApply": "Owner 放行後才可進入受控套用",
|
||||
"reviewPostApplyVerifier": "先審查套用後 verifier,再談執行",
|
||||
"prepareKmPlaybookWriteback": "準備 verifier 通過後的 KM / PlayBook 回寫"
|
||||
},
|
||||
"evidence": {
|
||||
"autoRepair": "Auto-repair",
|
||||
@@ -10217,6 +10221,17 @@
|
||||
"closureBlockedReason": "阻擋:{reason}",
|
||||
"closureOwnerFields": "Owner 放行欄位",
|
||||
"closureReadbackAssets": "只讀回查資產",
|
||||
"ownerReleasePackageTitle": "Owner 放行包",
|
||||
"releaseVerifierPackageTitle": "Verifier 放行前檢",
|
||||
"ownerReleasePacketValue": "packets {packets} · approved {approved}",
|
||||
"maintenanceRollbackValue": "維護 {maintenance} · rollback {rollback}",
|
||||
"operatorActionValue": "operator actions {actions}",
|
||||
"verifierPackageValue": "verifier {verifier} · ready {ready}",
|
||||
"liveApplyHoldValue": "live apply hold {holds} · passed {passed}",
|
||||
"ownerAuthorizedValue": "owner authorized {authorized}",
|
||||
"closureTaskBoardTitle": "閉環任務板",
|
||||
"closureTaskOwner": "Owner:{owner}",
|
||||
"runtimeWrite": "runtime_write={value}",
|
||||
"checklistTitle": "Owner 審查清單",
|
||||
"forbiddenTitle": "禁止動作",
|
||||
"gates": {
|
||||
@@ -10239,10 +10254,25 @@
|
||||
"finalCandidateReadback": "最終候選回查",
|
||||
"releaseVerifierPreflight": "Verifier 放行前檢查"
|
||||
},
|
||||
"closureTasks": {
|
||||
"ownerReleasePacketReview": "審 Owner 放行包",
|
||||
"maintenanceRollbackOwner": "維護 / Rollback",
|
||||
"controlledExecutionAuthorization": "受控執行授權",
|
||||
"postApplyVerifierPreflight": "套用後 Verifier",
|
||||
"kmPlaybookTrustWritebackPlan": "KM / PlayBook 回寫"
|
||||
},
|
||||
"closureStatuses": {
|
||||
"blockedBeforeOwnerRelease": "Owner 放行前受阻",
|
||||
"noWriteRehearsal": "無寫入演練",
|
||||
"readOnly": "只讀"
|
||||
"readOnly": "只讀",
|
||||
"ownerReviewRequired": "需 Owner 審查",
|
||||
"preflightReviewRequired": "需前檢審查",
|
||||
"readyForOwnerReview": "可送 Owner 審查",
|
||||
"approvalRequired": "需批准",
|
||||
"blockedByPolicy": "政策阻擋",
|
||||
"blockedBeforeRuntimeGate": "Runtime gate 前受阻",
|
||||
"blockedUntilVerifierPasses": "Verifier 通過前受阻",
|
||||
"snapshotUnavailable": "快照不可用"
|
||||
},
|
||||
"statuses": {
|
||||
"passed": "已通過",
|
||||
|
||||
@@ -166,6 +166,75 @@ export interface AwoooPStatusChain {
|
||||
asset_id?: string | null;
|
||||
status?: string | null;
|
||||
}>;
|
||||
owner_release_package?: {
|
||||
schema_version?: string | null;
|
||||
status?: string | null;
|
||||
source_schema_version?: string | null;
|
||||
source_task_id?: string | null;
|
||||
runtime_authority?: string | null;
|
||||
packet_count?: number | null;
|
||||
approval_required_count?: number | null;
|
||||
blocked_count?: number | null;
|
||||
owner_release_approved_count?: number | null;
|
||||
maintenance_window_approved_count?: number | null;
|
||||
rollback_owner_confirmed_count?: number | null;
|
||||
operator_action_count?: number | null;
|
||||
packets?: Array<{
|
||||
id?: string | null;
|
||||
display_name?: string | null;
|
||||
owner_agent?: string | null;
|
||||
status?: string | null;
|
||||
summary?: string | null;
|
||||
runtime_write_allowed?: boolean | null;
|
||||
}>;
|
||||
operator_actions?: Array<{
|
||||
id?: string | null;
|
||||
owner_agent?: string | null;
|
||||
status?: string | null;
|
||||
summary?: string | null;
|
||||
runtime_write_allowed?: boolean | null;
|
||||
}>;
|
||||
};
|
||||
release_verifier_package?: {
|
||||
schema_version?: string | null;
|
||||
status?: string | null;
|
||||
source_schema_version?: string | null;
|
||||
source_task_id?: string | null;
|
||||
runtime_authority?: string | null;
|
||||
verifier_count?: number | null;
|
||||
rollback_count?: number | null;
|
||||
maintenance_hold_count?: number | null;
|
||||
live_apply_hold_count?: number | null;
|
||||
owner_release_authorized_count?: number | null;
|
||||
post_release_verifier_ready_count?: number | null;
|
||||
live_apply_release_pass_count?: number | null;
|
||||
operator_action_count?: number | null;
|
||||
verifier_gates?: Array<{
|
||||
id?: string | null;
|
||||
display_name?: string | null;
|
||||
owner_agent?: string | null;
|
||||
status?: string | null;
|
||||
summary?: string | null;
|
||||
runtime_write_allowed?: boolean | null;
|
||||
}>;
|
||||
operator_actions?: Array<{
|
||||
id?: string | null;
|
||||
owner_agent?: string | null;
|
||||
status?: string | null;
|
||||
summary?: string | null;
|
||||
runtime_write_allowed?: boolean | null;
|
||||
}>;
|
||||
};
|
||||
closure_tasks?: Array<{
|
||||
key?: string | null;
|
||||
status?: string | null;
|
||||
owner_agent?: string | null;
|
||||
source_asset_id?: string | null;
|
||||
work_item_id?: string | null;
|
||||
summary?: string | null;
|
||||
next_step?: string | null;
|
||||
runtime_write_allowed?: boolean | null;
|
||||
}>;
|
||||
};
|
||||
candidate?: {
|
||||
catalog_id?: string | null;
|
||||
@@ -312,6 +381,11 @@ export function AwoooPStatusChainPanel({
|
||||
run_or_review_post_execution_verification: t("nextActions.reviewVerifier"),
|
||||
monitor_for_regression: t("nextActions.monitorRegression"),
|
||||
collect_evidence_or_wait: t("nextActions.collectEvidenceOrWait"),
|
||||
review_owner_release_packet_and_evidence_refs: t("nextActions.reviewOwnerReleasePacket"),
|
||||
collect_maintenance_window_and_rollback_owner: t("nextActions.collectMaintenanceRollback"),
|
||||
owner_release_required_before_controlled_apply: t("nextActions.ownerReleaseBeforeApply"),
|
||||
review_post_apply_verifier_before_any_apply: t("nextActions.reviewPostApplyVerifier"),
|
||||
prepare_km_and_playbook_writeback_after_verified_execution: t("nextActions.prepareKmPlaybookWriteback"),
|
||||
};
|
||||
return labels[key] ?? valueOrEmpty(value, emptyLabel);
|
||||
};
|
||||
@@ -466,6 +540,9 @@ export function AwoooPStatusChainPanel({
|
||||
const closureGates = closureReadiness?.gates ?? [];
|
||||
const closureReadbackAssets = closureReadiness?.readback_assets ?? [];
|
||||
const closureOwnerFields = closureReadiness?.required_owner_fields ?? [];
|
||||
const closureOwnerPackage = closureReadiness?.owner_release_package;
|
||||
const closureVerifierPackage = closureReadiness?.release_verifier_package;
|
||||
const closureTasks = closureReadiness?.closure_tasks ?? [];
|
||||
const ownerReviewChecklist = automationHandoff?.owner_review_checklist ?? [];
|
||||
const forbiddenActions = automationHandoff?.forbidden_actions ?? [];
|
||||
const sourceToolchainTone: SourceFlowTone = sourceCorrelation
|
||||
@@ -603,9 +680,27 @@ export function AwoooPStatusChainPanel({
|
||||
blocked_before_owner_release: t("applyGate.closureStatuses.blockedBeforeOwnerRelease"),
|
||||
no_write_rehearsal: t("applyGate.closureStatuses.noWriteRehearsal"),
|
||||
read_only: t("applyGate.closureStatuses.readOnly"),
|
||||
owner_review_required: t("applyGate.closureStatuses.ownerReviewRequired"),
|
||||
preflight_review_required: t("applyGate.closureStatuses.preflightReviewRequired"),
|
||||
ready_for_owner_review: t("applyGate.closureStatuses.readyForOwnerReview"),
|
||||
approval_required: t("applyGate.closureStatuses.approvalRequired"),
|
||||
blocked_by_policy: t("applyGate.closureStatuses.blockedByPolicy"),
|
||||
blocked_before_runtime_gate: t("applyGate.closureStatuses.blockedBeforeRuntimeGate"),
|
||||
blocked_until_verifier_passes: t("applyGate.closureStatuses.blockedUntilVerifierPasses"),
|
||||
snapshot_unavailable: t("applyGate.closureStatuses.snapshotUnavailable"),
|
||||
};
|
||||
return labels[String(status ?? "")] ?? handoffStatusLabel(status);
|
||||
};
|
||||
const closureTaskLabel = (key: string | null | undefined) => {
|
||||
const labels: Record<string, string> = {
|
||||
owner_release_packet_review: t("applyGate.closureTasks.ownerReleasePacketReview"),
|
||||
maintenance_window_rollback_owner: t("applyGate.closureTasks.maintenanceRollbackOwner"),
|
||||
controlled_execution_authorization: t("applyGate.closureTasks.controlledExecutionAuthorization"),
|
||||
post_apply_verifier_preflight: t("applyGate.closureTasks.postApplyVerifierPreflight"),
|
||||
km_playbook_trust_writeback_plan: t("applyGate.closureTasks.kmPlaybookTrustWritebackPlan"),
|
||||
};
|
||||
return labels[String(key ?? "")] ?? valueOrEmpty(key, emptyLabel);
|
||||
};
|
||||
const handoffWorkItemHref = automationHandoff?.work_item_id
|
||||
? `/awooop/work-items?project_id=awoooi&work_item_id=${encodeURIComponent(automationHandoff.work_item_id)}${automationHandoff.source_id ? `&incident_id=${encodeURIComponent(automationHandoff.source_id)}` : ""}`
|
||||
: null;
|
||||
@@ -917,6 +1012,117 @@ export function AwoooPStatusChainPanel({
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="grid gap-px bg-[#e0ddd4] lg:grid-cols-2">
|
||||
<div className="min-w-0 bg-white px-4 py-3">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<p className="text-xs font-semibold text-[#77736a]">{t("applyGate.ownerReleasePackageTitle")}</p>
|
||||
<p className="mt-1 truncate font-mono text-sm font-semibold text-[#141413]" title={valueOrEmpty(closureOwnerPackage?.runtime_authority, emptyLabel)}>
|
||||
{valueOrEmpty(closureOwnerPackage?.source_task_id, emptyLabel)}
|
||||
</p>
|
||||
</div>
|
||||
<span className="shrink-0 border border-[#d8d3c7] bg-[#faf9f3] px-2 py-0.5 font-mono text-[11px] text-[#5f5b52]">
|
||||
{closureStatusLabel(closureOwnerPackage?.status)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-3 grid gap-2 sm:grid-cols-3">
|
||||
<span className="border border-[#ece8dd] bg-[#faf9f3] px-2 py-1 font-mono text-[11px] text-[#141413]">
|
||||
{t("applyGate.ownerReleasePacketValue", {
|
||||
packets: closureOwnerPackage?.packet_count ?? 0,
|
||||
approved: closureOwnerPackage?.owner_release_approved_count ?? 0,
|
||||
})}
|
||||
</span>
|
||||
<span className="border border-[#ece8dd] bg-[#faf9f3] px-2 py-1 font-mono text-[11px] text-[#141413]">
|
||||
{t("applyGate.maintenanceRollbackValue", {
|
||||
maintenance: closureOwnerPackage?.maintenance_window_approved_count ?? 0,
|
||||
rollback: closureOwnerPackage?.rollback_owner_confirmed_count ?? 0,
|
||||
})}
|
||||
</span>
|
||||
<span className="border border-[#ece8dd] bg-[#faf9f3] px-2 py-1 font-mono text-[11px] text-[#141413]">
|
||||
{t("applyGate.operatorActionValue", {
|
||||
actions: closureOwnerPackage?.operator_action_count ?? 0,
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="min-w-0 bg-white px-4 py-3">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<p className="text-xs font-semibold text-[#77736a]">{t("applyGate.releaseVerifierPackageTitle")}</p>
|
||||
<p className="mt-1 truncate font-mono text-sm font-semibold text-[#141413]" title={valueOrEmpty(closureVerifierPackage?.runtime_authority, emptyLabel)}>
|
||||
{valueOrEmpty(closureVerifierPackage?.source_task_id, emptyLabel)}
|
||||
</p>
|
||||
</div>
|
||||
<span className="shrink-0 border border-[#d8d3c7] bg-[#faf9f3] px-2 py-0.5 font-mono text-[11px] text-[#5f5b52]">
|
||||
{closureStatusLabel(closureVerifierPackage?.status)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-3 grid gap-2 sm:grid-cols-3">
|
||||
<span className="border border-[#ece8dd] bg-[#faf9f3] px-2 py-1 font-mono text-[11px] text-[#141413]">
|
||||
{t("applyGate.verifierPackageValue", {
|
||||
verifier: closureVerifierPackage?.verifier_count ?? 0,
|
||||
ready: closureVerifierPackage?.post_release_verifier_ready_count ?? 0,
|
||||
})}
|
||||
</span>
|
||||
<span className="border border-[#ece8dd] bg-[#faf9f3] px-2 py-1 font-mono text-[11px] text-[#141413]">
|
||||
{t("applyGate.liveApplyHoldValue", {
|
||||
holds: closureVerifierPackage?.live_apply_hold_count ?? 0,
|
||||
passed: closureVerifierPackage?.live_apply_release_pass_count ?? 0,
|
||||
})}
|
||||
</span>
|
||||
<span className="border border-[#ece8dd] bg-[#faf9f3] px-2 py-1 font-mono text-[11px] text-[#141413]">
|
||||
{t("applyGate.ownerAuthorizedValue", {
|
||||
authorized: closureVerifierPackage?.owner_release_authorized_count ?? 0,
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t border-[#e0ddd4] bg-[#fbfaf5] px-4 py-2">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
<p className="text-xs font-semibold text-[#141413]">{t("applyGate.closureTaskBoardTitle")}</p>
|
||||
<span className="border border-[#d8d3c7] bg-white px-2 py-0.5 font-mono text-[11px] text-[#5f5b52]">
|
||||
{closureTasks.length}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid gap-px bg-[#e0ddd4] lg:grid-cols-5">
|
||||
{(closureTasks.length ? closureTasks : [{ key: emptyLabel, status: emptyLabel, owner_agent: emptyLabel, summary: emptyLabel, source_asset_id: emptyLabel, next_step: emptyLabel, runtime_write_allowed: false }]).map((task, index) => (
|
||||
<div key={`${task.key}-${index}`} className="min-w-0 bg-white px-4 py-3">
|
||||
<div className="flex min-w-0 items-start gap-3">
|
||||
<span className={cn(
|
||||
"flex h-8 w-8 shrink-0 items-center justify-center border",
|
||||
sourceFlowToneClass(handoffTone(task.status))
|
||||
)}>
|
||||
<Route className="h-4 w-4" aria-hidden="true" />
|
||||
</span>
|
||||
<div className="min-w-0">
|
||||
<p className="text-xs font-semibold text-[#77736a]">{closureTaskLabel(task.key)}</p>
|
||||
<p className="mt-1 truncate font-mono text-sm font-semibold text-[#141413]" title={closureStatusLabel(task.status)}>
|
||||
{closureStatusLabel(task.status)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="mt-2 truncate font-mono text-xs text-[#5f5b52]" title={valueOrEmpty(task.owner_agent, emptyLabel)}>
|
||||
{t("applyGate.closureTaskOwner", { owner: valueOrEmpty(task.owner_agent, emptyLabel) })}
|
||||
</p>
|
||||
<p className="mt-1 truncate font-mono text-xs text-[#141413]" title={valueOrEmpty(task.summary, emptyLabel)}>
|
||||
{valueOrEmpty(task.summary, emptyLabel)}
|
||||
</p>
|
||||
<p className="mt-1 break-all font-mono text-[11px] text-[#77736a]" title={valueOrEmpty(task.source_asset_id, emptyLabel)}>
|
||||
{valueOrEmpty(task.source_asset_id, emptyLabel)}
|
||||
</p>
|
||||
<p className="mt-2 text-[11px] leading-4 text-[#5f5b52]" title={valueOrEmpty(task.next_step, emptyLabel)}>
|
||||
{nextActionLabel(task.next_step)}
|
||||
</p>
|
||||
<span className="mt-2 inline-flex border border-[#e2a29b] bg-[#fff0ef] px-2 py-0.5 font-mono text-[11px] text-[#9f2f25]">
|
||||
{t("applyGate.runtimeWrite", {
|
||||
value: boolValue(task.runtime_write_allowed, emptyLabel),
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="grid gap-px bg-[#e0ddd4] lg:grid-cols-2">
|
||||
<div className="min-w-0 bg-white px-4 py-3">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
|
||||
Reference in New Issue
Block a user