From 5382ce348f02ba345aba7afd3d25d2b26948b8c3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 19 Jul 2026 11:01:45 +0800 Subject: [PATCH] feat(sre): track commitments and correlate Agent99 cards --- ...re_k3s_controlled_automation_work_items.py | 135 +++++++- apps/api/src/services/telegram_gateway.py | 100 +++++- ...est_agent99_dispatch_receipt_projection.py | 88 +++++ ...3s_controlled_automation_work_items_api.py | 72 +++- .../test_telegram_gateway_error_sanitizer.py | 33 ++ .../tests/test_telegram_message_templates.py | 23 +- apps/web/messages/en.json | 52 +++ apps/web/messages/zh-TW.json | 52 +++ .../web/src/app/[locale]/awooop/runs/page.tsx | 3 + .../app/[locale]/awooop/work-items/page.tsx | 3 + .../src/app/[locale]/knowledge-base/page.tsx | 5 + .../sre-ai-agent-commitments-panel.test.ts | 79 +++++ .../awooop/sre-ai-agent-commitments-panel.tsx | 326 ++++++++++++++++++ .../awooop/sre-ai-agent-commitments.ts | 60 ++++ ...ent-conversation-commitments.snapshot.json | 108 ++++++ 15 files changed, 1120 insertions(+), 19 deletions(-) create mode 100644 apps/web/src/components/awooop/__tests__/sre-ai-agent-commitments-panel.test.ts create mode 100644 apps/web/src/components/awooop/sre-ai-agent-commitments-panel.tsx create mode 100644 apps/web/src/components/awooop/sre-ai-agent-commitments.ts create mode 100644 docs/operations/sre-ai-agent-conversation-commitments.snapshot.json diff --git a/apps/api/src/services/sre_k3s_controlled_automation_work_items.py b/apps/api/src/services/sre_k3s_controlled_automation_work_items.py index acfc63208..29719a16c 100644 --- a/apps/api/src/services/sre_k3s_controlled_automation_work_items.py +++ b/apps/api/src/services/sre_k3s_controlled_automation_work_items.py @@ -12,7 +12,13 @@ from src.services.snapshot_paths import default_operations_dir _DEFAULT_OPERATIONS_DIR = default_operations_dir(Path(__file__)) _SNAPSHOT_FILE = "sre-k3s-controlled-automation-work-items.snapshot.json" +_CONVERSATION_COMMITMENTS_FILE = ( + "sre-ai-agent-conversation-commitments.snapshot.json" +) _SCHEMA_VERSION = "sre_k3s_controlled_automation_work_items_v1" +_CONVERSATION_COMMITMENTS_SCHEMA_VERSION = ( + "sre_ai_agent_conversation_commitments_v1" +) _PROGRAM_ID = "AIA-SRE-P0-20260715" _PIPELINE = [ "Alert", @@ -45,6 +51,23 @@ _VALID_STATUSES = { "policy_active", "runtime_closed", } +_VALID_COMMITMENT_STATUSES = { + "analysis_or_governance_complete", + "source_implemented_runtime_pending", + "in_progress", + "not_started_or_no_current_evidence", + "superseded", +} +_REQUIRED_COMMITMENT_CATEGORIES = { + "fixed_architecture", + "ai_provider", + "telegram", + "named_incident", + "knowledge_closure", + "infrastructure", + "work_management", + "delivery_efficiency", +} _REQUIRED_IMMEDIATE_EXECUTION_ORDER = [ "AIA-SRE-017", "AIA-SRE-002", @@ -88,7 +111,6 @@ def load_sre_k3s_controlled_automation_work_items( raise ValueError(f"{path}: unexpected schema_version") if payload.get("program_id") != _PROGRAM_ID: raise ValueError(f"{path}: unexpected program_id") - architecture = _dict(payload, "architecture", path) if architecture.get("pipeline") != _PIPELINE: raise ValueError(f"{path}: architecture.pipeline order changed") @@ -225,6 +247,10 @@ def load_sre_k3s_controlled_automation_work_items( f"{sorted(unknown_dependencies)}" ) + payload["conversation_commitment_registry"] = ( + _load_conversation_commitment_registry(directory, known_ids) + ) + immediate_queue = _list(payload, "immediate_execution_queue", path) queue_ids: list[str] = [] for position, row in enumerate(immediate_queue, start=1): @@ -320,6 +346,9 @@ def build_sre_k3s_program_projection(payload: dict[str, Any]) -> dict[str, Any]: "rollups": verifier_registry["rollups"], }, "agent99_bridge": payload["agent99_host_operations_bridge"], + "conversation_commitments": payload[ + "conversation_commitment_registry" + ]["rollups"], "full_readback_api": ( "/api/v1/agents/sre-k3s-controlled-automation-work-items" ), @@ -338,3 +367,107 @@ def _list(payload: dict[str, Any], key: str, path: Path) -> list[Any]: if not isinstance(value, list): raise ValueError(f"{path}: {key} must be an array") return value + + +def _load_conversation_commitment_registry( + operations_dir: Path, + known_work_item_ids: set[str], +) -> dict[str, Any]: + path = operations_dir / _CONVERSATION_COMMITMENTS_FILE + with path.open(encoding="utf-8") as handle: + payload = json.load(handle) + + if not isinstance(payload, dict): + raise ValueError(f"{path}: expected JSON object") + if payload.get("schema_version") != _CONVERSATION_COMMITMENTS_SCHEMA_VERSION: + raise ValueError(f"{path}: unexpected schema_version") + if payload.get("program_id") != _PROGRAM_ID: + raise ValueError(f"{path}: unexpected program_id") + if payload.get("authoritative_for_execution_order") is not False: + raise ValueError(f"{path}: companion registry cannot reorder execution") + if payload.get("raw_conversation_embedded") is not False: + raise ValueError(f"{path}: raw conversation must not be embedded") + if payload.get("secret_values_recorded") is not False: + raise ValueError(f"{path}: secret values must not be recorded") + + commitments = _list(payload, "commitments", path) + expected_ids = [f"AIA-CONV-{index:03d}" for index in range(1, 71)] + observed_ids: list[str] = [] + observed_categories: set[str] = set() + status_counts: dict[str, int] = {} + for index, row in enumerate(commitments, start=1): + if not isinstance(row, dict): + raise ValueError(f"{path}: commitments[{index - 1}] must be an object") + missing = { + "id", + "category", + "status", + "title", + "linked_work_items", + "terminal_condition", + } - row.keys() + if missing: + raise ValueError( + f"{path}: commitment missing fields: {sorted(missing)}" + ) + commitment_id = str(row["id"]) + status = str(row["status"]) + category = str(row["category"]) + if status not in _VALID_COMMITMENT_STATUSES: + raise ValueError(f"{path}: {commitment_id}.status invalid") + if category not in _REQUIRED_COMMITMENT_CATEGORIES: + raise ValueError(f"{path}: {commitment_id}.category invalid") + linked_work_items = row["linked_work_items"] + if not isinstance(linked_work_items, list) or not linked_work_items: + raise ValueError( + f"{path}: {commitment_id}.linked_work_items required" + ) + unknown_links = set(linked_work_items) - known_work_item_ids + if unknown_links: + raise ValueError( + f"{path}: {commitment_id} links unknown work items: " + f"{sorted(unknown_links)}" + ) + if not str(row["title"]).strip() or not str( + row["terminal_condition"] + ).strip(): + raise ValueError(f"{path}: {commitment_id} text fields required") + observed_ids.append(commitment_id) + observed_categories.add(category) + status_counts[status] = status_counts.get(status, 0) + 1 + + if observed_ids != expected_ids: + raise ValueError(f"{path}: commitments must be AIA-CONV-001..070") + if observed_categories != _REQUIRED_COMMITMENT_CATEGORIES: + raise ValueError(f"{path}: commitment category coverage mismatch") + + known_commitment_ids = set(observed_ids) + for row in commitments: + superseded_by = str(row.get("superseded_by") or "") + if row["status"] == "superseded": + if superseded_by not in known_commitment_ids: + raise ValueError( + f"{path}: {row['id']}.superseded_by must reference a commitment" + ) + elif superseded_by: + raise ValueError( + f"{path}: {row['id']}.superseded_by only allowed for superseded rows" + ) + + rollups = _dict(payload, "rollups", path) + if rollups.get("total_commitments") != len(commitments): + raise ValueError(f"{path}: rollups.total_commitments mismatch") + if rollups.get("by_status") != status_counts: + raise ValueError(f"{path}: rollups.by_status mismatch") + superseded_count = status_counts.get("superseded", 0) + if rollups.get("active_or_completed_commitments") != ( + len(commitments) - superseded_count + ): + raise ValueError( + f"{path}: rollups.active_or_completed_commitments mismatch" + ) + if rollups.get("product_runtime_closed_commitments") != 0: + raise ValueError( + f"{path}: conversation commitments cannot claim runtime closure" + ) + return payload diff --git a/apps/api/src/services/telegram_gateway.py b/apps/api/src/services/telegram_gateway.py index 13410e516..ec7f28e1d 100644 --- a/apps/api/src/services/telegram_gateway.py +++ b/apps/api/src/services/telegram_gateway.py @@ -426,6 +426,12 @@ _HOST_RESOURCE_TARGET_RE = re.compile( _HOST_RESOURCE_ALERTNAME_RE = re.compile(r"\balertname\s*=\s*\"?(?P[A-Za-z0-9_.:-]+)\"?") _HOST_RESOURCE_HOST_LABEL_RE = re.compile(r"\bhost\s*=\s*\"?(?P[A-Za-z0-9_.:-]+)\"?") _HOST_RESOURCE_RULE_LABEL_RE = re.compile(r"\brule\s*=\s*\"?(?P[A-Za-z0-9_.:-]+)\"?") +_HOST_RESOURCE_RUN_ID_RE = re.compile( + r"^\s*(?:run|run_id|automation_run_id)\s*[::=]\s*" + r"(?:)?(?P[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-" + r"[0-9a-f]{12})(?:)?\s*$", + re.IGNORECASE | re.MULTILINE, +) _HOST_PROCESS_LINE_RE = re.compile( r"^\s*(?P\S+)\s+" r"(?P\d+)\s+" @@ -447,8 +453,9 @@ _AI_SIGNAL_TARGET_LABEL_RE = re.compile( r"\b(?:host|target|service|domain|agent\.name|node|route)\s*=\s*\"?(?P[A-Za-z0-9_.:-]+)\"?" ) _ALERT_CARD_CODE_VALUE_RE = re.compile( - r"(?P