feat(ai): verify canonical learning writeback
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m42s
CD Pipeline / build-and-deploy (push) Successful in 6m29s
CD Pipeline / post-deploy-checks (push) Successful in 1m48s
AI 技術雷達監控 / ai-technology-watch (push) Successful in 45s
AWOOOI Harbor 110 Local Repair / workflow-shape (push) Successful in 1s
AWOOOI Harbor 110 Local Repair / harbor-110-local-repair (push) Successful in 50s

This commit is contained in:
ogt
2026-07-11 07:40:48 +08:00
parent cf426e0e36
commit 3f6f7a67ce
12 changed files with 1199 additions and 86 deletions

View File

@@ -4,6 +4,7 @@ from __future__ import annotations
import hashlib
import json
import os
import re
from typing import Any
@@ -48,6 +49,122 @@ def _json_dict(value: Any) -> dict[str, Any]:
return {}
def resolve_ansible_playbook_identity(
catalog_id: str,
) -> dict[str, Any] | None:
"""Resolve one allowlisted catalog action to its canonical PlayBook row."""
catalog = get_ansible_catalog_item(catalog_id)
if not catalog:
return None
playbook_path = str(catalog.get("playbook_path") or "")
canonical_id = canonical_ansible_playbook_id(catalog_id)
identity_payload = {
"canonical_playbook_id": canonical_id,
"catalog_id": catalog_id,
"playbook_path": playbook_path,
}
return {
"schema_version": "ansible_playbook_identity_v1",
**identity_payload,
"identity_fingerprint": hashlib.sha256(
json.dumps(
identity_payload,
ensure_ascii=True,
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
).hexdigest(),
}
def _playbook_row_fingerprint(playbook: PlaybookRecord) -> str:
updated_at = playbook.updated_at.isoformat() if playbook.updated_at else None
payload = {
"playbook_id": playbook.playbook_id,
"project_id": playbook.project_id,
"version": int(playbook.version or 0),
"success_count": int(playbook.success_count or 0),
"failure_count": int(playbook.failure_count or 0),
"trust_score": round(float(playbook.trust_score or 0.0), 6),
"updated_at": updated_at,
}
return hashlib.sha256(
json.dumps(
payload,
ensure_ascii=True,
sort_keys=True,
separators=(",", ":"),
).encode("utf-8")
).hexdigest()
def _playbook_trust_readback(
*,
playbook: PlaybookRecord,
identity: dict[str, Any],
verification_result: str,
) -> dict[str, Any]:
return {
"schema_version": "ansible_playbook_trust_writeback_v2",
"identity_schema_version": identity["schema_version"],
"identity_fingerprint": identity["identity_fingerprint"],
"canonical_playbook_id": playbook.playbook_id,
"catalog_id": identity["catalog_id"],
"playbook_path": identity["playbook_path"],
"playbook_row_version": int(playbook.version or 0),
"playbook_row_updated_at": (
playbook.updated_at.isoformat() if playbook.updated_at else None
),
"playbook_row_fingerprint": _playbook_row_fingerprint(playbook),
"success_count": int(playbook.success_count or 0),
"failure_count": int(playbook.failure_count or 0),
"trust_observation_count": (
int(playbook.success_count or 0)
+ int(playbook.failure_count or 0)
),
"trust_score": round(float(playbook.trust_score or 0.0), 6),
"verification_result": verification_result,
"learning_recorded": True,
"trust_updated": True,
"repository_write_acknowledged": True,
"repository_readback_verified": True,
"operation_receipt_readback_verified": True,
"durable_write_acknowledged": True,
"source_table": "playbooks",
"writer_source_sha": os.getenv("AWOOOI_BUILD_COMMIT_SHA", "")
.strip()
.lower()
or None,
"raw_log_payload_stored": False,
"secret_value_stored": False,
}
def _existing_trust_readback_matches(
existing_trust: dict[str, Any],
*,
playbook: PlaybookRecord,
identity: dict[str, Any],
) -> bool:
return bool(
existing_trust.get("schema_version")
== "ansible_playbook_trust_writeback_v2"
and existing_trust.get("canonical_playbook_id")
== identity["canonical_playbook_id"]
and existing_trust.get("identity_fingerprint")
== identity["identity_fingerprint"]
and existing_trust.get("playbook_row_version")
== int(playbook.version or 0)
and existing_trust.get("playbook_row_fingerprint")
== _playbook_row_fingerprint(playbook)
and existing_trust.get("repository_readback_verified") is True
and existing_trust.get("operation_receipt_readback_verified") is True
and existing_trust.get("learning_recorded") is True
and existing_trust.get("trust_updated") is True
)
async def record_ansible_playbook_trust_writeback(
*,
project_id: str,
@@ -62,9 +179,20 @@ async def record_ansible_playbook_trust_writeback(
"""Atomically update canonical PlayBook trust and its learning receipt."""
catalog = get_ansible_catalog_item(catalog_id)
if not catalog:
identity = resolve_ansible_playbook_identity(catalog_id)
if not catalog or identity is None:
return None
canonical_id = canonical_ansible_playbook_id(catalog_id)
canonical_id = str(identity["canonical_playbook_id"])
canonical_playbook_path = str(identity["playbook_path"])
if str(playbook_path) != canonical_playbook_path:
logger.warning(
"ansible_learning_playbook_identity_mismatch",
incident_id=incident_id,
catalog_id=catalog_id,
apply_op_id=apply_op_id,
)
return None
playbook_path = canonical_playbook_path
success = verification_result == "success"
now = now_taipei()
@@ -89,11 +217,6 @@ async def record_ansible_playbook_trust_writeback(
existing_trust = _json_dict(
existing_output.get("playbook_trust_writeback")
)
if (
existing_trust.get("durable_write_acknowledged") is True
and existing_trust.get("canonical_playbook_id") == canonical_id
):
return existing_trust
selected = await db.execute(
select(PlaybookRecord)
@@ -101,6 +224,31 @@ async def record_ansible_playbook_trust_writeback(
.with_for_update()
)
playbook = selected.scalar_one_or_none()
if existing_row:
if playbook is None:
logger.warning(
"ansible_learning_receipt_playbook_row_missing",
incident_id=incident_id,
catalog_id=catalog_id,
apply_op_id=apply_op_id,
)
return None
if _existing_trust_readback_matches(
existing_trust,
playbook=playbook,
identity=identity,
):
return existing_trust
logger.warning(
"ansible_learning_receipt_readback_not_verified",
incident_id=incident_id,
catalog_id=catalog_id,
apply_op_id=apply_op_id,
receipt_schema_version=existing_trust.get(
"schema_version"
),
)
return None
if playbook is None:
playbook = PlaybookRecord(
playbook_id=canonical_id,
@@ -161,6 +309,11 @@ async def record_ansible_playbook_trust_writeback(
db.add(playbook)
await db.flush()
source_incident_ids = list(playbook.source_incident_ids or [])
if incident_id and incident_id not in source_incident_ids:
source_incident_ids.append(incident_id)
playbook.source_incident_ids = source_incident_ids
if success:
playbook.success_count = int(playbook.success_count or 0) + 1
playbook.trust_score = 0.9 * float(playbook.trust_score) + 0.1
@@ -170,84 +323,100 @@ async def record_ansible_playbook_trust_writeback(
playbook.trust_score = max(0.0, min(1.0, playbook.trust_score))
playbook.last_used_at = now
playbook.updated_at = now
trust_writeback = {
"schema_version": "ansible_playbook_trust_writeback_v1",
"canonical_playbook_id": canonical_id,
"catalog_id": catalog_id,
"success_count": playbook.success_count,
"failure_count": playbook.failure_count,
"trust_score": round(float(playbook.trust_score), 6),
"verification_result": verification_result,
"durable_write_acknowledged": True,
"source_table": "playbooks",
}
await db.flush()
await db.refresh(playbook)
trust_writeback = _playbook_trust_readback(
playbook=playbook,
identity=identity,
verification_result=verification_result,
)
input_payload = {
"schema_version": "ansible_learning_writeback_receipt_v2",
"schema_version": "ansible_learning_writeback_receipt_v3",
"automation_run_id": automation_run_id,
"incident_id": incident_id,
"catalog_id": catalog_id,
"canonical_playbook_id": canonical_id,
"identity_schema_version": identity["schema_version"],
"identity_fingerprint": identity["identity_fingerprint"],
"playbook_path": playbook_path,
"apply_op_id": apply_op_id,
"verification_result": verification_result,
"learning_repository": "playbooks",
"writer_source_sha": trust_writeback["writer_source_sha"],
"stores_raw_logs": False,
"stores_secret_values": False,
}
output_payload = {
"learning_recorded": True,
"trust_updated": True,
"success": success,
"repository_readback_verified": True,
"playbook_trust_writeback": trust_writeback,
}
if existing_row:
await db.execute(
text("""
UPDATE automation_operation_log
SET status = 'success',
input = coalesce(input, '{}'::jsonb) || CAST(:input AS jsonb),
output = coalesce(output, '{}'::jsonb) || CAST(:output AS jsonb),
error = NULL
WHERE op_id = CAST(:op_id AS uuid)
"""),
{
"input": json.dumps(input_payload, ensure_ascii=False),
"output": json.dumps(output_payload, ensure_ascii=False),
"op_id": str(existing_row["op_id"]),
},
)
else:
await db.execute(
text("""
INSERT INTO automation_operation_log (
operation_type, actor, status, incident_id,
input, output, dry_run_result, parent_op_id, tags
) VALUES (
'ansible_learning_writeback_recorded',
'ansible_controlled_apply_worker',
'success',
:incident_db_id,
CAST(:input AS jsonb),
CAST(:output AS jsonb),
'{}'::jsonb,
CAST(:parent_op_id AS uuid),
:tags
)
"""),
{
"incident_db_id": incident_db_id,
"input": json.dumps(input_payload, ensure_ascii=False),
"output": json.dumps(output_payload, ensure_ascii=False),
"parent_op_id": apply_op_id,
"tags": [
"ansible",
"controlled_apply",
"learning_writeback",
"playbook_trust",
"ai_agent_auto_execution",
f"automation_run_id:{automation_run_id}",
],
},
await db.execute(
text("""
INSERT INTO automation_operation_log (
operation_type, actor, status, incident_id,
input, output, dry_run_result, parent_op_id, tags
) VALUES (
'ansible_learning_writeback_recorded',
'ansible_controlled_apply_worker',
'success',
:incident_db_id,
CAST(:input AS jsonb),
CAST(:output AS jsonb),
'{}'::jsonb,
CAST(:parent_op_id AS uuid),
:tags
)
"""),
{
"incident_db_id": incident_db_id,
"input": json.dumps(input_payload, ensure_ascii=False),
"output": json.dumps(output_payload, ensure_ascii=False),
"parent_op_id": apply_op_id,
"tags": [
"ansible",
"controlled_apply",
"learning_writeback",
"playbook_trust",
"ai_agent_auto_execution",
f"automation_run_id:{automation_run_id}",
],
},
)
await db.flush()
operation_readback = await db.execute(
text("""
SELECT status, input, output
FROM automation_operation_log
WHERE operation_type
= 'ansible_learning_writeback_recorded'
AND parent_op_id = CAST(:apply_op_id AS uuid)
ORDER BY created_at DESC
LIMIT 1
"""),
{"apply_op_id": apply_op_id},
)
operation_row = operation_readback.mappings().first()
operation_output = _json_dict(
operation_row.get("output") if operation_row else None
)
operation_trust = _json_dict(
operation_output.get("playbook_trust_writeback")
)
if not (
operation_row
and operation_row.get("status") == "success"
and operation_output.get("learning_recorded") is True
and operation_output.get("trust_updated") is True
and operation_output.get("repository_readback_verified")
is True
and operation_trust.get("playbook_row_fingerprint")
== trust_writeback["playbook_row_fingerprint"]
):
raise RuntimeError(
"ansible_learning_operation_receipt_readback_failed"
)
return trust_writeback
except Exception as exc: