feat(sre): add 24h alert learning corpus

This commit is contained in:
Your Name
2026-07-22 21:54:31 +08:00
parent df50718ac5
commit 3243bc33e0
5 changed files with 467 additions and 26 deletions

View File

@@ -1256,8 +1256,9 @@ async def get_sre_k3s_controlled_automation_work_items() -> dict[str, Any]:
description=(
"唯讀彙總 PostgreSQL incident、run 與 provider budget receipts計算 "
"MTTA、MTTR、誤判、復發、人工介入、verifier、rollback、freshness、"
"asset coverage 與模型成本。缺 evidence 時保持 partial不執行修復、"
"不呼叫模型、不讀 secret。"
"asset coverage 與模型成本,並輸出去識別、可重算分母的 24h learning "
"corpus。缺 evidence 時保持 partialshadow/no-write 不計 production "
"execution不執行修復、不呼叫模型、不讀 secret。"
),
)
async def get_sre_automation_slo_scorecard_24h(

View File

@@ -2,6 +2,8 @@
from __future__ import annotations
import hashlib
import json
import re
from collections.abc import Mapping, Sequence
from datetime import UTC, datetime, timedelta
@@ -25,6 +27,8 @@ _INCIDENT_SAMPLES_SQL = text(
COALESCE(operation.incident_id, operation.id::text) AS sample_id,
operation.event_type::text AS event_type,
operation.created_at,
operation.action_detail,
operation.success,
operation.context,
incident.resolved_at AS incident_resolved_at,
COALESCE(
@@ -43,6 +47,66 @@ _INCIDENT_SAMPLES_SQL = text(
NULLIF(operation.context ->> 'project_id', ''),
'awoooi'
) = :project_id
), execution_evidence AS (
SELECT
*,
(
event_type IN (
'EXECUTION_STARTED', 'EXECUTION_COMPLETED',
'CHANGE_APPLIED'
)
) AS execution_like_event,
(
event_type = 'CHANGE_APPLIED'
OR lower(COALESCE(context ->> 'execution_mode', ''))
IN ('production', 'controlled_apply')
OR lower(COALESCE(context ->> 'execution_kind', ''))
= 'executable'
OR lower(COALESCE(context ->> 'side_effect_performed', ''))
IN ('true', '1', 'yes')
OR lower(COALESCE(context ->> 'repair_executed', ''))
IN ('true', '1', 'yes')
OR lower(COALESCE(
context ->> 'controlled_apply_executed', ''
)) IN ('true', '1', 'yes')
OR lower(COALESCE(context ->> 'runtime_apply_executed', ''))
IN ('true', '1', 'yes')
OR lower(COALESCE(action_detail, '')) LIKE 'controlled_apply_%'
) AS production_execution_evidence,
(
lower(COALESCE(context ->> 'is_shadow', 'false'))
IN ('true', '1', 'yes')
OR lower(COALESCE(context ->> 'execution_mode', '')) IN (
'shadow', 'canary', 'check_mode', 'dry_run',
'read_only', 'no_write', 'no_write_replay',
'controlled_retry_check_mode_replay',
'stdin_boundary_check_mode_replay'
)
OR lower(COALESCE(context ->> 'execution_kind', ''))
IN ('shadow', 'no_action', 'no_write', 'no_write_replay')
OR lower(COALESCE(context ->> 'side_effect_performed', ''))
IN ('false', '0', 'no')
OR lower(COALESCE(action_detail, '')) LIKE ANY (ARRAY[
'%shadow%', '%no_write%', '%check_mode%', '%dry_run%'
])
) AS execution_exclusion_evidence
FROM scoped_events
), classified_events AS (
SELECT
*,
(
execution_like_event
AND production_execution_evidence
AND NOT execution_exclusion_evidence
) AS production_execution_event,
(
execution_like_event
AND NOT (
production_execution_evidence
AND NOT execution_exclusion_evidence
)
) AS excluded_execution_event
FROM execution_evidence
), per_incident AS (
SELECT
sample_id,
@@ -79,17 +143,24 @@ _INCIDENT_SAMPLES_SQL = text(
) AS recurrence,
bool_or(event_type IN ('USER_ACTION', 'MANUAL_FIX_RECORDED'))
AS human_intervention,
bool_or(event_type IN (
'AUTO_REPAIR_TRIGGERED', 'EXECUTION_STARTED',
'EXECUTION_COMPLETED', 'CHANGE_APPLIED'
)) AS execution_observed,
bool_or(event_type = 'AUTO_REPAIR_TRIGGERED') AS repair_observed,
bool_or(
event_type = 'AUTO_REPAIR_TRIGGERED' AND success IS TRUE
) AS repair_accepted,
bool_or(production_execution_event) AS execution_observed,
bool_or(excluded_execution_event)
AS shadow_or_no_write_execution_observed,
bool_or(
context ? 'verifier_passed'
OR context ? 'post_verifier_passed'
OR context ? 'verification_result'
) AS verifier_observed,
bool_or(
lower(COALESCE(context ->> 'verifier_passed', 'false'))
IN ('true', '1', 'yes')
OR lower(COALESCE(
context ->> 'post_verifier_passed', 'false'
)) IN ('true', '1', 'yes')
OR lower(COALESCE(context ->> 'verification_result', ''))
IN ('success', 'passed', 'verified')
) AS verifier_passed,
@@ -109,7 +180,7 @@ _INCIDENT_SAMPLES_SQL = text(
AND COALESCE(context ->> 'asset_identity_status', 'resolved')
<> 'asset_identity_unresolved'
) AS asset_identity_resolved
FROM scoped_events
FROM classified_events
GROUP BY sample_id
)
SELECT *
@@ -196,6 +267,21 @@ def _average(values: Sequence[float]) -> float | None:
return round(sum(values) / len(values), 2) if values else None
def _iso_timestamp(value: object) -> str | None:
parsed = _as_datetime(value)
return parsed.isoformat() if parsed else None
def _sha256_json(value: object) -> str:
canonical = json.dumps(
value,
ensure_ascii=True,
separators=(",", ":"),
sort_keys=True,
)
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
def _metrics_for(
samples: Sequence[Mapping[str, Any]],
) -> tuple[dict[str, Any], dict[str, int]]:
@@ -213,8 +299,11 @@ def _metrics_for(
total = len(samples)
false_observed = sum(_flag(row.get("false_positive_observed")) for row in samples)
recurrence_observed = sum(_flag(row.get("recurrence_observed")) for row in samples)
execution_observed = sum(_flag(row.get("execution_observed")) for row in samples)
verifier_observed = sum(_flag(row.get("verifier_observed")) for row in samples)
execution_samples = [row for row in samples if _flag(row.get("execution_observed"))]
execution_observed = len(execution_samples)
verifier_observed = sum(
_flag(row.get("verifier_observed")) for row in execution_samples
)
freshness_observed = sum(_flag(row.get("freshness_observed")) for row in samples)
denominators = {
"alert_count": total,
@@ -222,8 +311,17 @@ def _metrics_for(
"mttr_sample_count": len(mttr),
"false_positive_classified_count": false_observed,
"recurrence_classified_count": recurrence_observed,
"repair_observed_count": sum(
_flag(row.get("repair_observed")) for row in samples
),
"repair_accepted_count": sum(
_flag(row.get("repair_accepted")) for row in samples
),
"execution_observed_count": execution_observed,
"verifier_observed_count": verifier_observed,
"shadow_or_no_write_execution_excluded_count": sum(
_flag(row.get("shadow_or_no_write_execution_observed")) for row in samples
),
"freshness_observed_count": freshness_observed,
}
metrics = {
@@ -242,11 +340,15 @@ def _metrics_for(
total,
),
"verifier_pass_rate": _rate(
sum(_flag(row.get("verifier_passed")) for row in samples),
sum(
_flag(row.get("verifier_observed"))
and _flag(row.get("verifier_passed"))
for row in execution_samples
),
verifier_observed,
),
"rollback_rate": _rate(
sum(_flag(row.get("rollback_performed")) for row in samples),
sum(_flag(row.get("rollback_performed")) for row in execution_samples),
execution_observed,
),
"signal_freshness_slo_pass_rate": _rate(
@@ -267,6 +369,198 @@ def _metrics_for(
return metrics, denominators
def _build_learning_corpus(
*,
project_id: str,
incident_samples: Sequence[Mapping[str, Any]],
safe_runs: Mapping[str, int],
provider_usage: Mapping[str, Any],
window_start: datetime,
window_end: datetime,
) -> dict[str, Any]:
records: list[dict[str, Any]] = []
missing_source_identity_count = 0
for sample in incident_samples:
source_identity = str(sample.get("sample_id") or "").strip()
if not source_identity:
missing_source_identity_count += 1
identity_material = f"{project_id}\0{source_identity}".encode()
record_id = (
f"sha256:{hashlib.sha256(identity_material).hexdigest()}"
if source_identity
else ""
)
lane = str(sample.get("lane") or "unclassified")[:64]
execution_observed = _flag(sample.get("execution_observed"))
verifier_observed = execution_observed and _flag(
sample.get("verifier_observed")
)
recurrence_observed = _flag(sample.get("recurrence_observed"))
false_positive_observed = _flag(sample.get("false_positive_observed"))
freshness_observed = _flag(sample.get("freshness_observed"))
asset_identity_resolved = _flag(sample.get("asset_identity_resolved"))
received_at = _iso_timestamp(sample.get("received_at"))
resolved_at = _iso_timestamp(sample.get("resolved_at"))
learning_eligible = bool(
record_id
and received_at
and resolved_at
and lane != "unclassified"
and recurrence_observed
and false_positive_observed
and freshness_observed
and asset_identity_resolved
and (not execution_observed or verifier_observed)
)
records.append(
{
"record_id": record_id,
"lane": lane,
"timestamps": {
"alert_received_at": received_at,
"first_action_at": _iso_timestamp(sample.get("first_action_at")),
"resolved_at": resolved_at,
},
"outcomes": {
"repair_observed": _flag(sample.get("repair_observed")),
"repair_accepted": _flag(sample.get("repair_accepted")),
"production_execution_observed": execution_observed,
"shadow_or_no_write_execution_excluded": _flag(
sample.get("shadow_or_no_write_execution_observed")
),
"verifier_observed": verifier_observed,
"verifier_passed": (
_flag(sample.get("verifier_passed"))
if verifier_observed
else None
),
"recurrence_observed": recurrence_observed,
"recurrence": (
_flag(sample.get("recurrence")) if recurrence_observed else None
),
"false_positive_observed": false_positive_observed,
"false_positive": (
_flag(sample.get("false_positive"))
if false_positive_observed
else None
),
"human_intervention": _flag(sample.get("human_intervention")),
"rollback_performed": (
_flag(sample.get("rollback_performed"))
if execution_observed
else None
),
},
"quality": {
"signal_freshness_observed": freshness_observed,
"signal_freshness_slo_passed": (
_flag(sample.get("freshness_passed"))
if freshness_observed
else None
),
"asset_identity_resolved": asset_identity_resolved,
},
"learning_eligible": learning_eligible,
}
)
records.sort(
key=lambda row: (
str(row["timestamps"].get("alert_received_at") or ""),
str(row.get("record_id") or ""),
)
)
denominators = {
"alert_count": len(records),
"repair_observed_count": sum(
row["outcomes"]["repair_observed"] for row in records
),
"repair_accepted_count": sum(
row["outcomes"]["repair_accepted"] for row in records
),
"production_execution_observed_count": sum(
row["outcomes"]["production_execution_observed"] for row in records
),
"verifier_observed_execution_count": sum(
row["outcomes"]["verifier_observed"] for row in records
),
"recurrence_classified_count": sum(
row["outcomes"]["recurrence_observed"] for row in records
),
"controlled_non_shadow_run_count": int(
safe_runs.get("controlled_runs_with_steps") or 0
),
"provider_call_count": int(provider_usage.get("call_count") or 0),
}
excluded_from_execution = {
"shadow_run_count": max(
int(safe_runs.get("total_runs") or 0)
- int(safe_runs.get("non_shadow_runs") or 0),
0,
),
"non_shadow_no_step_run_count": max(
int(safe_runs.get("non_shadow_runs") or 0)
- int(safe_runs.get("controlled_runs_with_steps") or 0),
0,
),
"alert_count_with_shadow_or_no_write_execution_evidence": sum(
row["outcomes"]["shadow_or_no_write_execution_excluded"] for row in records
),
}
eligible_record_count = sum(row["learning_eligible"] for row in records)
status = (
"no_runtime_sample_fail_closed"
if not records
else "partial_missing_receipt_coverage"
if eligible_record_count < len(records) or missing_source_identity_count
else "learning_corpus_ready_same_run_verification_pending"
)
fingerprint_payload = {
"window": {
"start": window_start.astimezone(UTC).isoformat(),
"end": window_end.astimezone(UTC).isoformat(),
},
"records": records,
"denominators": denominators,
"excluded_from_execution": excluded_from_execution,
"provider_usage": provider_usage,
}
return {
"schema_version": "sre_alert_learning_corpus_v1",
"window": fingerprint_payload["window"],
"status": status,
"runtime_closed": False,
"read_only": True,
"raw_context_included": False,
"record_count": len(records),
"learning_eligible_record_count": eligible_record_count,
"missing_source_identity_count": missing_source_identity_count,
"records": records,
"denominators": denominators,
"provider_usage": dict(provider_usage),
"provider_cost_usd": str(provider_usage.get("cost_usd") or "0.0000"),
"excluded_from_execution": excluded_from_execution,
"recomputation_contract": {
"alert": "count(records)",
"repair": "count(records where outcomes.repair_observed=true)",
"execution": (
"count(records where outcomes.production_execution_observed=true); "
"shadow, canary, check-mode, dry-run and no-write are excluded"
),
"verifier": (
"count(executed records where outcomes.verifier_observed=true)"
),
"recurrence": ("count(records where outcomes.recurrence_observed=true)"),
"cost": (
"sum(provider_usage.rows.call_count) and "
"sum(provider_usage.rows.cost_usd)"
),
},
"corpus_sha256": _sha256_json(fingerprint_payload),
"same_run_receipt_verification": "pending",
}
def build_automation_slo_scorecard(
*,
project_id: str,
@@ -349,6 +643,21 @@ def build_automation_slo_scorecard(
"controlled_failed_runs",
)
}
provider_usage = {
"rows": costs,
"call_count": sum(row["call_count"] for row in costs),
"prompt_tokens": sum(row["prompt_tokens"] for row in costs),
"completion_tokens": sum(row["completion_tokens"] for row in costs),
"cost_usd": f"{total_cost:.4f}",
}
learning_corpus = _build_learning_corpus(
project_id=project_id,
incident_samples=incident_samples,
safe_runs=safe_runs,
provider_usage=provider_usage,
window_start=window_start,
window_end=window_end,
)
return {
"schema_version": SCHEMA_VERSION,
"project_id": project_id,
@@ -363,13 +672,8 @@ def build_automation_slo_scorecard(
"denominators": denominators,
"lane_scorecards": lane_scorecards,
"runs": safe_runs,
"provider_usage": {
"rows": costs,
"call_count": sum(row["call_count"] for row in costs),
"prompt_tokens": sum(row["prompt_tokens"] for row in costs),
"completion_tokens": sum(row["completion_tokens"] for row in costs),
"cost_usd": f"{total_cost:.4f}",
},
"provider_usage": provider_usage,
"learning_corpus": learning_corpus,
"missing_metrics": missing_metrics,
"coverage_blockers": coverage_blockers,
"evidence": {

View File

@@ -1,3 +1,4 @@
import json
from datetime import UTC, datetime, timedelta
from unittest.mock import AsyncMock
@@ -20,8 +21,10 @@ def _sample(
action_seconds: int,
resolve_seconds: int,
positive: bool,
sample_id: str | None = None,
) -> dict:
return {
"sample_id": sample_id or f"{lane}:{int(start.timestamp())}",
"lane": lane,
"received_at": start,
"first_action_at": start + timedelta(seconds=action_seconds),
@@ -31,7 +34,10 @@ def _sample(
"recurrence_observed": True,
"recurrence": positive,
"human_intervention": positive,
"repair_observed": True,
"repair_accepted": True,
"execution_observed": True,
"shadow_or_no_write_execution_observed": False,
"verifier_observed": True,
"verifier_passed": not positive,
"rollback_performed": positive,
@@ -53,6 +59,7 @@ def test_scorecard_computes_all_required_metrics_and_provider_cost() -> None:
action_seconds=10,
resolve_seconds=100,
positive=False,
sample_id="INC-REAL-1",
),
_sample(
start + timedelta(hours=2),
@@ -60,6 +67,7 @@ def test_scorecard_computes_all_required_metrics_and_provider_cost() -> None:
action_seconds=20,
resolve_seconds=200,
positive=True,
sample_id="INC-REAL-2",
),
],
run_summary={
@@ -115,6 +123,11 @@ def test_scorecard_computes_all_required_metrics_and_provider_cost() -> None:
assert scorecard["runs"]["completed_runs"] == 4
assert scorecard["runs"]["controlled_completed_runs"] == 1
assert scorecard["runs"]["controlled_failed_runs"] == 1
assert scorecard["denominators"]["repair_observed_count"] == 2
assert scorecard["denominators"]["repair_accepted_count"] == 2
assert scorecard["denominators"]["execution_observed_count"] == 2
assert scorecard["denominators"]["verifier_observed_count"] == 2
assert scorecard["denominators"]["shadow_or_no_write_execution_excluded_count"] == 0
assert scorecard["provider_usage"] == {
"rows": [
{
@@ -139,6 +152,40 @@ def test_scorecard_computes_all_required_metrics_and_provider_cost() -> None:
"completion_tokens": 350,
"cost_usd": "0.0123",
}
corpus = scorecard["learning_corpus"]
assert corpus["schema_version"] == "sre_alert_learning_corpus_v1"
assert corpus["window"] == {
"start": start.astimezone(UTC).isoformat(),
"end": end.astimezone(UTC).isoformat(),
}
assert corpus["runtime_closed"] is False
assert corpus["read_only"] is True
assert corpus["raw_context_included"] is False
assert corpus["record_count"] == 2
assert corpus["learning_eligible_record_count"] == 1
assert corpus["status"] == "partial_missing_receipt_coverage"
assert corpus["denominators"] == {
"alert_count": 2,
"repair_observed_count": 2,
"repair_accepted_count": 2,
"production_execution_observed_count": 2,
"verifier_observed_execution_count": 2,
"recurrence_classified_count": 2,
"controlled_non_shadow_run_count": 2,
"provider_call_count": 4,
}
assert corpus["provider_usage"] == scorecard["provider_usage"]
assert corpus["provider_cost_usd"] == "0.0123"
assert corpus["excluded_from_execution"] == {
"shadow_run_count": 2,
"non_shadow_no_step_run_count": 1,
"alert_count_with_shadow_or_no_write_execution_evidence": 0,
}
assert all(row["record_id"].startswith("sha256:") for row in corpus["records"])
assert len(corpus["corpus_sha256"]) == 64
serialized_corpus = json.dumps(corpus)
assert "INC-REAL-1" not in serialized_corpus
assert "INC-REAL-2" not in serialized_corpus
assert scorecard["evidence"] == {
"source": "postgresql_read_only_aggregate",
"same_run_receipt_verification": "pending",
@@ -171,6 +218,90 @@ def test_scorecard_does_not_false_green_missing_runtime_samples() -> None:
"verifier_pass_rate",
}
assert scorecard["provider_usage"]["cost_usd"] == "0.0000"
assert scorecard["learning_corpus"]["status"] == ("no_runtime_sample_fail_closed")
assert scorecard["learning_corpus"]["record_count"] == 0
def test_learning_corpus_excludes_shadow_and_no_write_from_execution() -> None:
end = datetime(2026, 7, 19, 4, 0, tzinfo=UTC)
real = _sample(
end - timedelta(hours=2),
lane="kubernetes_workload",
action_seconds=10,
resolve_seconds=100,
positive=False,
sample_id="INC-PRODUCTION",
)
excluded = _sample(
end - timedelta(hours=1),
lane="host_systemd",
action_seconds=20,
resolve_seconds=200,
positive=False,
sample_id="INC-SHADOW",
)
excluded["execution_observed"] = False
excluded["shadow_or_no_write_execution_observed"] = True
scorecard = build_automation_slo_scorecard(
project_id="awoooi",
incident_samples=[real, excluded],
run_summary={
"total_runs": 3,
"non_shadow_runs": 2,
"controlled_runs_with_steps": 1,
"completed_runs": 3,
"failed_runs": 0,
"controlled_completed_runs": 1,
"controlled_failed_runs": 0,
},
provider_cost_rows=[],
window_start=end - timedelta(hours=24),
window_end=end,
)
assert scorecard["denominators"]["execution_observed_count"] == 1
assert scorecard["denominators"]["verifier_observed_count"] == 1
assert scorecard["denominators"]["shadow_or_no_write_execution_excluded_count"] == 1
corpus = scorecard["learning_corpus"]
assert corpus["status"] == ("learning_corpus_ready_same_run_verification_pending")
assert corpus["denominators"]["production_execution_observed_count"] == 1
assert corpus["denominators"]["verifier_observed_execution_count"] == 1
assert corpus["excluded_from_execution"] == {
"shadow_run_count": 1,
"non_shadow_no_step_run_count": 1,
"alert_count_with_shadow_or_no_write_execution_evidence": 1,
}
excluded_record = next(
row
for row in corpus["records"]
if row["outcomes"]["shadow_or_no_write_execution_excluded"]
)
assert excluded_record["outcomes"]["production_execution_observed"] is False
assert excluded_record["outcomes"]["verifier_observed"] is False
def test_incident_query_classifies_only_non_shadow_runtime_execution() -> None:
statement = " ".join(str(scorecard_module._INCIDENT_SAMPLES_SQL).split())
execution_events = statement.split("), execution_evidence AS (", 1)[1].split(
"AS execution_like_event", 1
)[0]
assert "AUTO_REPAIR_TRIGGERED" not in execution_events
assert (
"event_type IN ( 'EXECUTION_STARTED', 'EXECUTION_COMPLETED', "
"'CHANGE_APPLIED' )" in execution_events
)
assert "AS production_execution_evidence" in statement
assert "AS execution_exclusion_evidence" in statement
assert "context ->> 'is_shadow'" in statement
assert "context ->> 'execution_mode'" in statement
assert "context ->> 'side_effect_performed'" in statement
assert "'%no_write%'" in statement
assert "context ->> 'execution_mode', 'production'" not in statement
assert "context ->> 'execution_kind', 'executable'" not in statement
assert "bool_or(production_execution_event) AS execution_observed" in statement
assert "context ? 'post_verifier_passed'" in statement
def test_run_query_excludes_shadow_and_no_step_runs_from_controlled_outcomes() -> None:
@@ -182,8 +313,7 @@ def test_run_query_excludes_shadow_and_no_step_runs_from_controlled_outcomes() -
)
assert (
"WHERE is_shadow IS FALSE AND step_count > 0 "
"AND state IN ('failed', 'cancelled', 'timeout')"
in statement
"AND state IN ('failed', 'cancelled', 'timeout')" in statement
)

View File

@@ -155,8 +155,8 @@ def test_loader_returns_fixed_architecture_provider_order_and_agent99_bridge() -
"active_or_completed_commitments": 68,
"by_status": {
"analysis_or_governance_complete": 6,
"source_implemented_runtime_pending": 35,
"in_progress": 26,
"source_implemented_runtime_pending": 36,
"in_progress": 25,
"not_started_or_no_current_evidence": 1,
"superseded": 2,
},
@@ -206,6 +206,12 @@ def test_loader_returns_fixed_architecture_provider_order_and_agent99_bridge() -
assert "independent exit-code and freshness receipts" in " ".join(
commitments["AIA-CONV-041"]["source_evidence"]
)
assert commitments["AIA-CONV-045"]["status"] == (
"source_implemented_runtime_pending"
)
assert "deterministically recomputable" in " ".join(
commitments["AIA-CONV-045"]["source_evidence"]
)
assert "Host112" in commitments["AIA-CONV-049"]["title"]
assert commitments["AIA-CONV-049"]["status"] == (
"source_implemented_runtime_pending"

View File

@@ -1,7 +1,7 @@
{
"schema_version": "sre_ai_agent_conversation_commitments_v1",
"program_id": "AIA-SRE-P0-20260715",
"generated_at": "2026-07-22T18:49:36+08:00",
"generated_at": "2026-07-22T21:45:56+08:00",
"source_scope": "This Codex AI Agent task only; cross-task delegation envelopes are excluded.",
"authoritative_for_execution_order": false,
"raw_conversation_embedded": false,
@@ -63,7 +63,7 @@
{"id":"AIA-CONV-042","category":"knowledge_closure","status":"source_implemented_runtime_pending","title":"所有告警推理前整合 MCP 與 RAG context","linked_work_items":["AIA-SRE-012","AIA-SRE-015"],"terminal_condition":"每次推理記錄 context source IDs、freshness 與 retrieval receipt。","source_evidence":["all DecisionManager reasoning and early-return paths reserve one durable pre-inference MCP/RAG receipt before provider or Agent debate","receipt records exact MCP evidence or execution IDs, Knowledge entry IDs, observed timestamps and deterministic freshness without persisting raw context","Phase2, primary LLM and background PlayBook draft receive the same sanitized untrusted receipt-bound context","receipt persistence or context-source contract failure returns a deterministic no-provider no-executor no-runtime-mutation terminal"]},
{"id":"AIA-CONV-043","category":"knowledge_closure","status":"source_implemented_runtime_pending","title":"診斷、候選、執行、驗證沉澱至 KM/RAG/MCP/PlayBook","linked_work_items":["AIA-SRE-015"],"terminal_condition":"同 run 六個 durable write receipts 唯一、可讀回且與 target/source 一致。"},
{"id":"AIA-CONV-044","category":"knowledge_closure","status":"source_implemented_runtime_pending","title":"每項資產必須有 ID、owner、status、next action、來源與 receipt","linked_work_items":["AIA-SRE-001","AIA-SRE-002","AIA-SRE-015"],"terminal_condition":"Runs、Work Items 與 KB 只讀同一 canonical identity 與 evidence chain。"},
{"id":"AIA-CONV-045","category":"knowledge_closure","status":"in_progress","title":"24 小時 production 告警形成可學習 corpus","linked_work_items":["AIA-SRE-016"],"terminal_condition":"alert、repair、execution、verifier、recurrence 與 cost 分母可重算shadow 不冒充 execution。"},
{"id":"AIA-CONV-045","category":"knowledge_closure","status":"source_implemented_runtime_pending","title":"24 小時 production 告警形成可學習 corpus","linked_work_items":["AIA-SRE-016"],"terminal_condition":"alert、repair、execution、verifier、recurrence 與 cost 分母可重算shadow 不冒充 execution。","source_evidence":["project-scoped read-only 24h corpus projects only hashed alert identities and sanitized lifecycle labels","alert, repair, production execution, verifier, recurrence, controlled non-shadow run and provider-call cost denominators are deterministically recomputable","execution classifier and run projection exclude shadow, canary, check-mode, dry-run, no-write and non-step outcomes from production execution","missing identity, freshness, classification or verifier coverage remains partial and same-run production verification pending"]},
{"id":"AIA-CONV-046","category":"knowledge_closure","status":"source_implemented_runtime_pending","title":"Runs、Work Items、Knowledge Base 顯示完整關聯證據","linked_work_items":["AIA-SRE-001","AIA-SRE-015","AIA-SRE-017"],"terminal_condition":"API 與 desktop/mobile cockpit 對相同 run 顯示一致的資產與 receipt truth。"},
{"id":"AIA-CONV-047","category":"knowledge_closure","status":"in_progress","title":"Executor 不得是唯一 verifier","linked_work_items":["AIA-SRE-014"],"terminal_condition":"每個 mutating domain 都有不同 owner/identity 的 independent verifier。"},
{"id":"AIA-CONV-048","category":"knowledge_closure","status":"source_implemented_runtime_pending","title":"Telegram、callback、learning receipt 使用 durable production readback","linked_work_items":["AIA-SRE-015","AIA-SRE-017"],"terminal_condition":"source flag、HTTP 200 或 configured state 不得取代 production durable acknowledgement。"},
@@ -98,8 +98,8 @@
"active_or_completed_commitments": 68,
"by_status": {
"analysis_or_governance_complete": 6,
"source_implemented_runtime_pending": 35,
"in_progress": 26,
"source_implemented_runtime_pending": 36,
"in_progress": 25,
"not_started_or_no_current_evidence": 1,
"superseded": 2
},