fix(awooop): allow ansible learning receipt rows
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Failing after 16s
CD Pipeline / build-and-deploy (push) Has been skipped
CD Pipeline / post-deploy-checks (push) Has been skipped

This commit is contained in:
Your Name
2026-06-29 17:26:55 +08:00
parent f69607b58f
commit 876ba030d8
6 changed files with 106 additions and 4 deletions

View File

@@ -242,8 +242,14 @@ jobs:
;;
apps/api/src/services/ai_agent_autonomous_runtime_control.py)
;;
apps/api/src/services/awooop_ansible_audit_service.py)
;;
apps/api/src/services/awooop_ansible_check_mode_service.py)
;;
apps/api/migrations/adr090e_ansible_learning_writeback_operation_type.sql)
;;
apps/api/migrations/adr090e_ansible_learning_writeback_operation_type_down.sql)
;;
apps/api/src/services/auto_approve.py)
;;
apps/api/src/services/decision_fusion.py)
@@ -474,6 +480,7 @@ jobs:
src/services/awoooi_production_deploy_readback_blocker.py \
src/services/agent_replay_normalizer.py \
src/services/ai_agent_autonomous_runtime_control.py \
src/services/awooop_ansible_audit_service.py \
src/services/awooop_ansible_check_mode_service.py \
src/services/auto_repair_service.py \
src/services/auto_approve.py \

View File

@@ -0,0 +1,37 @@
-- ADR-090-E: automation_operation_log.operation_type adds Ansible learning writeback receipt
-- Created: 2026-06-29 Taipei
--
-- Purpose:
-- P1-C autonomous learning loop closure. This operation type records that
-- post-apply verifier output was accepted by LearningService / PlayBook trust
-- writeback after an Ansible controlled apply.
--
-- Safety:
-- This migration only expands the CHECK allowlist. It does not execute
-- Ansible, change incidents, read secrets, or alter retained data.
ALTER TABLE automation_operation_log
DROP CONSTRAINT IF EXISTS automation_operation_log_type_valid;
ALTER TABLE automation_operation_log
ADD CONSTRAINT automation_operation_log_type_valid CHECK (operation_type IN (
'monitor_configured','monitor_removed',
'alert_fired','alert_suppressed','alert_routed',
'rule_created','rule_updated','rule_matched','rule_rejected','rule_deprecated',
'playbook_generated','playbook_updated','playbook_executed',
'remediation_executed','remediation_verified','remediation_rolled_back',
'self_correction_attempted',
'km_created','km_updated','km_linked',
'asset_discovered','coverage_recalculated',
'capacity_recommendation','quota_enforced',
'notification_formatted',
'ansible_candidate_matched',
'ansible_check_mode_executed',
'ansible_apply_executed',
'ansible_learning_writeback_recorded',
'ansible_rollback_executed',
'ansible_execution_skipped'
));
COMMENT ON CONSTRAINT automation_operation_log_type_valid ON automation_operation_log IS
'ADR-090-E: allow Ansible learning writeback receipt rows for autonomous runtime closure.';

View File

@@ -0,0 +1,36 @@
-- ADR-090-E rollback: remove Ansible learning writeback receipt from operation_type allowlist.
-- Only apply after confirming no automation_operation_log rows use the operation type.
DO $$
BEGIN
IF EXISTS (
SELECT 1
FROM automation_operation_log
WHERE operation_type = 'ansible_learning_writeback_recorded'
LIMIT 1
) THEN
RAISE EXCEPTION 'cannot remove ansible_learning_writeback_recorded while rows still exist';
END IF;
END $$;
ALTER TABLE automation_operation_log
DROP CONSTRAINT IF EXISTS automation_operation_log_type_valid;
ALTER TABLE automation_operation_log
ADD CONSTRAINT automation_operation_log_type_valid CHECK (operation_type IN (
'monitor_configured','monitor_removed',
'alert_fired','alert_suppressed','alert_routed',
'rule_created','rule_updated','rule_matched','rule_rejected','rule_deprecated',
'playbook_generated','playbook_updated','playbook_executed',
'remediation_executed','remediation_verified','remediation_rolled_back',
'self_correction_attempted',
'km_created','km_updated','km_linked',
'asset_discovered','coverage_recalculated',
'capacity_recommendation','quota_enforced',
'notification_formatted',
'ansible_candidate_matched',
'ansible_check_mode_executed',
'ansible_apply_executed',
'ansible_rollback_executed',
'ansible_execution_skipped'
));

View File

@@ -28,6 +28,7 @@ ANSIBLE_OPERATION_TYPES = frozenset({
"ansible_candidate_matched",
"ansible_check_mode_executed",
"ansible_apply_executed",
"ansible_learning_writeback_recorded",
"ansible_rollback_executed",
"ansible_execution_skipped",
})

View File

@@ -1590,19 +1590,19 @@ def test_ansible_apply_operation_row_can_backfill_auto_repair_receipt() -> None:
assert result.duration_ms == 456
def test_ansible_apply_operation_row_reconstructs_from_columns_when_input_is_sparse() -> None:
def test_ansible_apply_operation_row_reconstructs_from_input_without_physical_columns() -> None:
reconstructed = _claim_from_apply_operation_row({
"op_id": "apply-op-2",
"parent_op_id": "check-op-2",
"incident_id": "INC-20260629-231F8E",
"status": "success",
"catalog_id": "ansible:188-momo-backup-user",
"playbook_path": "infra/ansible/playbooks/188-momo-backup-user.yml",
"risk_level": "low",
"input": {
"incident_id": "INC-20260629-231F8E",
"catalog_id": "ansible:188-momo-backup-user",
"source_candidate_op_id": "candidate-op-2",
"check_mode_op_id": "check-op-2",
"playbook_path": "infra/ansible/playbooks/188-momo-backup-user.yml",
"risk_level": "low",
},
"output": {"returncode": 0, "stdout_tail": "ok"},
"dry_run_result": {"apply_executed": True},
@@ -1624,6 +1624,9 @@ def test_ansible_apply_receipt_backfill_queries_existing_apply_rows() -> None:
assert "operation_type = 'ansible_apply_executed'" in source
assert "auto_repair_executions existing" in source
assert "executed_steps::text LIKE" in source
assert "apply.catalog_id" not in source
assert "apply.playbook_path" not in source
assert "apply.risk_level" not in source
def test_ansible_auto_repair_receipt_insert_casts_asyncpg_parameters() -> None:
@@ -1669,6 +1672,20 @@ def test_ansible_post_apply_km_writeback_is_idempotent_for_learning_backfill() -
assert "KnowledgeDBRepository" in source
def test_ansible_learning_writeback_operation_type_has_schema_migration() -> None:
migration = Path(
"apps/api/migrations/adr090e_ansible_learning_writeback_operation_type.sql"
).read_text()
down = Path(
"apps/api/migrations/adr090e_ansible_learning_writeback_operation_type_down.sql"
).read_text()
assert "ansible_learning_writeback_recorded" in migration
assert "automation_operation_log_type_valid" in migration
assert "DROP CONSTRAINT IF EXISTS automation_operation_log_type_valid" in migration
assert "cannot remove ansible_learning_writeback_recorded" in down
def test_ansible_live_controlled_apply_sends_telegram_receipt_but_backfill_does_not() -> None:
live_source = inspect.getsource(run_controlled_apply_for_claim)
backfill_source = inspect.getsource(backfill_missing_auto_repair_execution_receipts_once)

View File

@@ -88,7 +88,11 @@ def test_ai_autonomous_runtime_control_stays_on_controlled_runtime_profile() ->
def test_awooop_ansible_check_mode_stays_on_controlled_runtime_profile() -> None:
text = _workflow_text()
expected_sources = [
"apps/api/src/services/awooop_ansible_audit_service.py)",
"apps/api/src/services/awooop_ansible_check_mode_service.py)",
"apps/api/migrations/adr090e_ansible_learning_writeback_operation_type.sql)",
"apps/api/migrations/adr090e_ansible_learning_writeback_operation_type_down.sql)",
"src/services/awooop_ansible_audit_service.py",
"apps/api/tests/test_awooop_truth_chain_service.py)",
"src/services/awooop_ansible_check_mode_service.py",
"tests/test_awooop_truth_chain_service.py",