Files
awoooi/ops/agent99-truth-chain-index-executor-overlay.py
Your Name 9098b91757
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 1s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m54s
CD Pipeline / build-and-deploy (push) Successful in 15m28s
CD Pipeline / post-deploy-checks (push) Successful in 4m16s
feat(agent99): add digest-bound truth-chain index executor
2026-07-17 06:17:32 +08:00

426 lines
15 KiB
Python

#!/usr/bin/env python3
"""Apply the fixed truth-chain index set without accepting raw SQL."""
from __future__ import annotations
import argparse
import asyncio
import hashlib
import io
import json
import os
import re
import socket
import sys
import time
from contextlib import redirect_stderr, redirect_stdout
from typing import Any
from sqlalchemy import text
from src.db.base import get_engine
_SCHEMA_VERSION = "agent99_truth_chain_index_executor_v1"
_COMMAND_ID = "truth_chain_structured_indexes_v1"
_CANONICAL_ASSET = "database:awoooi:prod/truth-chain-structured-indexes"
_MIGRATION_SHA256 = (
"657089552ceed8de916d87135164b255c85edc154bf437f870b8c2bbdf14c509"
)
_SAFE_ID = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:@+-]{0,127}$")
_EXTENSION_OPERATIONS = (
("pg_trgm", "CREATE EXTENSION IF NOT EXISTS pg_trgm"),
("btree_gin", "CREATE EXTENSION IF NOT EXISTS btree_gin"),
)
_INDEX_OPERATIONS = (
(
"idx_aol_incident_text_created",
"""
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_aol_incident_text_created
ON automation_operation_log ((incident_id::text), created_at DESC)
WHERE incident_id IS NOT NULL
""",
),
(
"idx_aol_input_incident_created",
"""
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_aol_input_incident_created
ON automation_operation_log ((input ->> 'incident_id'), created_at DESC)
WHERE input ? 'incident_id'
""",
),
(
"idx_aol_output_incident_created",
"""
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_aol_output_incident_created
ON automation_operation_log ((output ->> 'incident_id'), created_at DESC)
WHERE output ? 'incident_id'
""",
),
(
"idx_aol_input_source_incidents_gin",
"""
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_aol_input_source_incidents_gin
ON automation_operation_log
USING GIN ((COALESCE(input #> '{source_refs,incident_ids}', '[]'::jsonb)))
""",
),
(
"idx_aol_output_source_incidents_gin",
"""
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_aol_output_source_incidents_gin
ON automation_operation_log
USING GIN ((COALESCE(output #> '{source_refs,incident_ids}', '[]'::jsonb)))
""",
),
(
"idx_outbound_source_incident_ids_gin",
"""
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_outbound_source_incident_ids_gin
ON awooop_outbound_message
USING GIN ((COALESCE(source_envelope #> '{source_refs,incident_ids}', '[]'::jsonb)))
""",
),
(
"idx_outbound_source_code_refs_gin",
"""
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_outbound_source_code_refs_gin
ON awooop_outbound_message
USING GIN ((COALESCE(source_envelope #> '{source_refs,code_refs}', '[]'::jsonb)))
""",
),
(
"idx_outbound_callback_incident_recent",
"""
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_outbound_callback_incident_recent
ON awooop_outbound_message (
project_id,
(source_envelope #>> '{callback_reply,incident_id}'),
queued_at DESC
)
WHERE source_envelope #>> '{callback_reply,incident_id}' IS NOT NULL
""",
),
(
"idx_outbound_alert_incident_recent",
"""
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_outbound_alert_incident_recent
ON awooop_outbound_message (
project_id,
(source_envelope #>> '{alert_notification,incident_id}'),
queued_at DESC
)
WHERE source_envelope #>> '{alert_notification,incident_id}' IS NOT NULL
""",
),
(
"idx_outbound_source_incident_recent",
"""
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_outbound_source_incident_recent
ON awooop_outbound_message (
project_id,
(source_envelope #>> '{source_refs,incident_id}'),
queued_at DESC
)
WHERE source_envelope #>> '{source_refs,incident_id}' IS NOT NULL
""",
),
(
"idx_outbound_top_incident_recent",
"""
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_outbound_top_incident_recent
ON awooop_outbound_message (
project_id,
(source_envelope ->> 'incident_id'),
queued_at DESC
)
WHERE source_envelope ->> 'incident_id' IS NOT NULL
""",
),
(
"idx_outbound_project_content_preview_trgm",
"""
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_outbound_project_content_preview_trgm
ON awooop_outbound_message
USING GIN (project_id, content_preview gin_trgm_ops)
WHERE content_preview IS NOT NULL
""",
),
)
_EXPECTED_EXTENSIONS = tuple(name for name, _ in _EXTENSION_OPERATIONS)
_EXPECTED_INDEXES = tuple(name for name, _ in _INDEX_OPERATIONS)
_CATALOG_SQL = text(
"""
SELECT index_class.relname AS index_name,
index_state.indisvalid,
index_state.indisready
FROM pg_catalog.pg_class AS index_class
JOIN pg_catalog.pg_index AS index_state
ON index_state.indexrelid = index_class.oid
WHERE index_class.relname IN (
'idx_aol_incident_text_created',
'idx_aol_input_incident_created',
'idx_aol_output_incident_created',
'idx_aol_input_source_incidents_gin',
'idx_aol_output_source_incidents_gin',
'idx_outbound_source_incident_ids_gin',
'idx_outbound_source_code_refs_gin',
'idx_outbound_callback_incident_recent',
'idx_outbound_alert_incident_recent',
'idx_outbound_source_incident_recent',
'idx_outbound_top_incident_recent',
'idx_outbound_project_content_preview_trgm'
)
"""
)
def _normalized_sql(value: str) -> str:
return re.sub(r"\s+", " ", value.strip()).lower()
def _sha256(value: str) -> str:
return hashlib.sha256(value.encode("utf-8")).hexdigest()
def _operation_digest() -> str:
statements = [sql for _, sql in (*_EXTENSION_OPERATIONS, *_INDEX_OPERATIONS)]
return _sha256("\n".join(_normalized_sql(sql) for sql in statements))
def _safe_error(exc: BaseException) -> dict[str, Any]:
database_error = getattr(exc, "orig", None)
sqlstate = str(
getattr(database_error, "sqlstate", None)
or getattr(database_error, "pgcode", None)
or ""
)
return {
"error_class": exc.__class__.__name__,
"database_error_class": (
database_error.__class__.__name__ if database_error else None
),
"sqlstate": sqlstate or None,
}
async def _catalog(connection: Any) -> dict[str, Any]:
extension_result = await connection.execute(
text(
"""
SELECT extname
FROM pg_catalog.pg_extension
WHERE extname IN ('pg_trgm', 'btree_gin')
ORDER BY extname
"""
)
)
extensions = {
str(row.get("extname") or "")
for row in extension_result.mappings().all()
}
index_result = await connection.execute(_CATALOG_SQL)
index_rows = list(index_result.mappings().all())
present = {str(row.get("index_name") or "") for row in index_rows}
valid_ready = {
str(row.get("index_name") or "")
for row in index_rows
if row.get("indisvalid") and row.get("indisready")
}
missing_extensions = sorted(set(_EXPECTED_EXTENSIONS) - extensions)
missing_indexes = sorted(set(_EXPECTED_INDEXES) - present)
invalid_or_unready = sorted(present - valid_ready)
return {
"expected_extension_count": len(_EXPECTED_EXTENSIONS),
"extension_count": len(extensions),
"missing_extensions": missing_extensions,
"expected_index_count": len(_EXPECTED_INDEXES),
"present_index_count": len(present),
"valid_ready_index_count": len(valid_ready),
"missing_indexes": missing_indexes,
"invalid_or_unready_indexes": invalid_or_unready,
"passed": not missing_extensions
and not missing_indexes
and not invalid_or_unready,
}
async def _execute_fixed_operation(
connection: Any,
*,
operation_type: str,
name: str,
sql: str,
) -> dict[str, Any]:
started = time.perf_counter()
try:
await connection.execute(text(sql))
except Exception as exc:
return {
"type": operation_type,
"name": name,
"statement_sha256": _sha256(_normalized_sql(sql)),
"duration_ms": round((time.perf_counter() - started) * 1000),
"success": False,
**_safe_error(exc),
}
return {
"type": operation_type,
"name": name,
"statement_sha256": _sha256(_normalized_sql(sql)),
"duration_ms": round((time.perf_counter() - started) * 1000),
"success": True,
"error_class": None,
"database_error_class": None,
"sqlstate": None,
}
async def _run(args: argparse.Namespace) -> dict[str, Any]:
for identity in (args.trace_id, args.run_id, args.work_item_id):
if not _SAFE_ID.fullmatch(identity):
raise ValueError("identity_invalid")
if not re.fullmatch(r"[0-9a-f]{40}", args.source_revision):
raise ValueError("source_revision_invalid")
if args.migration_sha256 != _MIGRATION_SHA256:
raise ValueError("migration_sha256_mismatch")
started = time.perf_counter()
operations: list[dict[str, Any]] = []
engine = get_engine()
async with engine.connect() as raw_connection:
connection = await raw_connection.execution_options(
isolation_level="AUTOCOMMIT"
)
before = await _catalog(connection)
if args.mode == "apply" and before["invalid_or_unready_indexes"]:
after = before
terminal = "blocked_invalid_or_unready_index"
else:
if args.mode == "apply":
await connection.execute(text("SET lock_timeout = '15s'"))
await connection.execute(text("SET statement_timeout = '10min'"))
for name, sql in _EXTENSION_OPERATIONS:
if name not in before["missing_extensions"]:
continue
operation = await _execute_fixed_operation(
connection,
operation_type="create_extension",
name=name,
sql=sql,
)
operations.append(operation)
if not operation["success"]:
break
if not operations or operations[-1]["success"]:
for name, sql in _INDEX_OPERATIONS:
if name not in before["missing_indexes"]:
continue
operation = await _execute_fixed_operation(
connection,
operation_type="create_index_concurrently",
name=name,
sql=sql,
)
operations.append(operation)
if not operation["success"]:
break
after = await _catalog(connection)
failed_operation = any(not operation["success"] for operation in operations)
if args.mode == "check":
terminal = "check_ready"
elif args.mode == "verify":
terminal = "verified_healthy" if after["passed"] else "verification_failed"
elif failed_operation:
terminal = "apply_failed"
else:
terminal = (
"apply_verified_local"
if after["passed"]
else "verification_failed"
)
process_identity = ":".join(
[socket.gethostname(), str(os.getpid()), args.run_id, args.mode]
)
write_attempted = bool(operations)
writes_performed = any(operation["success"] for operation in operations)
return {
"schema_version": _SCHEMA_VERSION,
"command_id": _COMMAND_ID,
"canonical_asset": _CANONICAL_ASSET,
"mode": args.mode,
"trace_id": args.trace_id,
"run_id": args.run_id,
"work_item_id": args.work_item_id,
"source_revision": args.source_revision,
"migration_sha256": args.migration_sha256,
"operation_digest_sha256": _operation_digest(),
"pod_name": socket.gethostname(),
"process_identity_sha256": _sha256(process_identity),
"elapsed_ms": round((time.perf_counter() - started) * 1000),
"catalog_before": before,
"catalog_after": after,
"operations": operations,
"write_attempted": write_attempted,
"writes_performed": writes_performed,
"raw_sql_accepted": False,
"raw_sql_emitted": False,
"query_parameters_emitted": False,
"secret_value_read": False,
"arbitrary_command_allowed": False,
"cross_domain_fallback_allowed": False,
"verifier": {
"passed": args.mode == "check" or bool(after["passed"]),
"target_ready": bool(after["passed"]),
},
"terminal": terminal,
}
def _parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument("--mode", choices=("check", "apply", "verify"), required=True)
parser.add_argument("--trace-id", required=True)
parser.add_argument("--run-id", required=True)
parser.add_argument("--work-item-id", required=True)
parser.add_argument("--source-revision", required=True)
parser.add_argument("--migration-sha256", required=True)
return parser.parse_args()
def main() -> int:
try:
application_output = io.StringIO()
with redirect_stdout(application_output), redirect_stderr(application_output):
receipt = asyncio.run(_run(_parse_args()))
except Exception as exc:
receipt = {
"schema_version": _SCHEMA_VERSION,
"command_id": _COMMAND_ID,
"terminal": "failed",
**_safe_error(exc),
"write_attempted": False,
"writes_performed": False,
"raw_sql_accepted": False,
"raw_sql_emitted": False,
"query_parameters_emitted": False,
"secret_value_read": False,
"arbitrary_command_allowed": False,
"cross_domain_fallback_allowed": False,
}
print(json.dumps(receipt, separators=(",", ":"), sort_keys=True))
return 0 if receipt.get("terminal") in {
"check_ready",
"apply_verified_local",
"verified_healthy",
} else 3
if __name__ == "__main__":
sys.exit(main())