fix(agent): keep completed milestones monotonic
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 1m21s
CD Pipeline / build-and-deploy (push) Successful in 6m48s
CD Pipeline / post-deploy-checks (push) Successful in 1m52s
AWOOOI Harbor 110 Local Repair / workflow-shape (push) Successful in 1s
AWOOOI Harbor 110 Local Repair / harbor-110-local-repair (push) Successful in 17s

This commit is contained in:
ogt
2026-07-11 17:58:56 +08:00
parent 3274162185
commit 09d630df2b
2 changed files with 200 additions and 0 deletions

View File

@@ -271,6 +271,14 @@ _AI_AUTOMATION_PROGRAM_DEPENDENCIES = {
"AIA-P0-010": ["AIA-P0-006", "AIA-P0-011"],
"AIA-P0-007": ["AIA-P0-002", "AIA-P0-003", "AIA-P0-004", "AIA-P0-005", "AIA-P0-006", "AIA-P0-008"],
}
_AI_AUTOMATION_MONOTONIC_CLOSURE_ORDER = (
"AIA-P0-001",
"AIA-P0-011",
"AIA-P0-002",
"AIA-P0-003",
"AIA-P0-004",
"AIA-P0-005",
)
_AI_SINGLE_WRITER_REQUIRED_CONTROL_IDS = (
"strict_runtime_same_run_closed",
"runtime_contract_compatible_with_deployed_source_boundary",
@@ -7300,6 +7308,59 @@ def _align_authoritative_current_p0_execution_focus(
)
def _apply_monotonic_program_closure_floor(
work_items: list[dict[str, Any]],
closure_proofs: list[dict[str, Any]],
) -> None:
deepest_closed_index = -1
for index, proof in enumerate(closure_proofs):
if proof.get("closed") is True:
deepest_closed_index = index
if deepest_closed_index < 0:
return
successor = closure_proofs[deepest_closed_index]
successor_work_item_id = str(successor.get("work_item_id") or "")
successor_run_id = str(successor.get("automation_run_id") or "")
evidence_refs = [
ref.strip()
for ref in _list(successor.get("evidence_refs"))
if isinstance(ref, str) and ref.strip()
]
if successor_run_id:
evidence_refs.insert(0, f"automation-run:{successor_run_id}")
evidence_refs = list(dict.fromkeys(ref for ref in evidence_refs if ref))
if not successor_work_item_id or not evidence_refs:
return
items_by_id = {str(item.get("id") or ""): item for item in work_items}
for work_item_id in _AI_AUTOMATION_MONOTONIC_CLOSURE_ORDER[
: deepest_closed_index + 1
]:
item = items_by_id[work_item_id]
if item.get("status") == "done":
continue
runtime_progress = _dict(item.get("runtime_progress"))
runtime_progress["latest_runtime_closed"] = (
runtime_progress.get("runtime_closed") is True
)
runtime_progress["monotonic_program_closed"] = True
runtime_progress["monotonic_successor_work_item_id"] = (
successor_work_item_id
)
item["runtime_progress"] = runtime_progress
item["status"] = "done"
item["completion_evidence"] = {
"source": "agent-autonomous-runtime-control",
"proof_kind": "monotonic_successor_runtime_closure",
"successor_work_item_id": successor_work_item_id,
"successor_automation_run_id": successor_run_id,
"latest_runtime_state_preserved": True,
}
item["runtime_evidence_refs"] = evidence_refs
def _apply_ai_automation_program_ledger(payload: dict[str, Any]) -> None:
summary = _dict(payload.setdefault("summary", {}))
rollups = _dict(payload.setdefault("rollups", {}))
@@ -7929,6 +7990,109 @@ def _apply_ai_automation_program_ledger(payload: dict[str, Any]) -> None:
or canonical_learning_work_item["status"] == "done"
):
retry_rollback_work_item["status"] = "in_progress"
_apply_monotonic_program_closure_floor(
work_items,
[
{
"work_item_id": "AIA-P0-001",
"closed": strict_runtime_closed,
"automation_run_id": summary.get(
"ai_agent_autonomous_runtime_automation_run_id"
),
"evidence_refs": [
"agent-autonomous-runtime-control:strict_runtime_completion"
],
},
{
"work_item_id": "AIA-P0-011",
"closed": trust_boundary_runtime_closed,
"automation_run_id": summary.get(
"ai_agent_autonomous_runtime_automation_run_id"
),
"evidence_refs": [
summary.get("ai_agent_executor_trust_boundary_receipt_ref")
],
},
{
"work_item_id": "AIA-P0-002",
"closed": (
single_writer_closed
and all(single_writer_controls.values())
and not list(
summary.get("ai_agent_single_writer_active_blockers")
or []
)
),
"automation_run_id": summary.get(
"ai_agent_single_writer_automation_run_id"
),
"evidence_refs": [
summary.get("ai_agent_single_writer_source_receipt_ref")
],
},
{
"work_item_id": "AIA-P0-003",
"closed": (
post_verifier_closed
and all(post_verifier_controls.values())
and not list(
summary.get(
"ai_agent_independent_post_verifier_active_blockers"
)
or []
)
),
"automation_run_id": summary.get(
"ai_agent_independent_post_verifier_automation_run_id"
),
"evidence_refs": [
summary.get(
"ai_agent_independent_post_verifier_source_receipt_ref"
),
summary.get(
"ai_agent_independent_post_verifier_evidence_id"
),
],
},
{
"work_item_id": "AIA-P0-004",
"closed": (
canonical_learning_closed
and all(canonical_learning_controls.values())
and not list(
summary.get("ai_agent_canonical_learning_active_blockers")
or []
)
),
"automation_run_id": summary.get(
"ai_agent_canonical_learning_automation_run_id"
),
"evidence_refs": [
summary.get("ai_agent_canonical_learning_source_receipt_ref"),
summary.get("ai_agent_canonical_learning_learning_operation_id"),
summary.get("ai_agent_canonical_learning_knowledge_entry_id"),
summary.get("ai_agent_canonical_learning_canonical_playbook_id"),
],
},
{
"work_item_id": "AIA-P0-005",
"closed": (
retry_rollback_closed
and all(retry_rollback_controls.values())
and not list(
summary.get("ai_agent_retry_rollback_active_blockers")
or []
)
),
"automation_run_id": summary.get(
"ai_agent_retry_rollback_automation_run_id"
),
"evidence_refs": summary.get(
"ai_agent_retry_rollback_evidence_refs"
),
},
],
)
priority_rank = {"P0": 0, "P1": 1, "P2": 2, "P3": 3}
for item in work_items:
item_id = str(item["id"])

View File

@@ -2393,6 +2393,42 @@ def test_ai_automation_retry_rollback_terminal_advances_order() -> None:
assert payload["current_p0_execution_focus"]["workplan_id"] == "AIA-P0-006"
def test_retry_terminal_keeps_prior_program_milestones_monotonic() -> None:
payload = load_latest_awoooi_priority_work_order_readback()
runtime_control = _autonomous_runtime_control_retry_rollback_closed()
verifier = runtime_control["autonomous_independent_post_verifier"]
verifier["closed"] = False
verifier["controls"]["verification_result_success"] = False
verifier["controls"]["all_postconditions_passed"] = False
verifier["active_blockers"] = [
"verification_result_success_not_verified",
"all_postconditions_passed_not_verified",
]
apply_ai_automation_live_closure_readbacks(
payload,
autonomous_runtime_control=runtime_control,
)
program = payload["ai_automation_program_ledger"]
items = {item["id"]: item for item in program["work_items"]}
verifier_item = items["AIA-P0-003"]
assert verifier_item["status"] == "done"
assert verifier_item["runtime_progress"]["runtime_closed"] is False
assert verifier_item["runtime_progress"]["latest_runtime_closed"] is False
assert verifier_item["runtime_progress"]["monotonic_program_closed"] is True
assert verifier_item["completion_evidence"]["proof_kind"] == (
"monotonic_successor_runtime_closure"
)
assert verifier_item["completion_evidence"]["successor_work_item_id"] == (
"AIA-P0-005"
)
assert program["summary"]["done_count"] == 6
assert program["summary"]["completion_percent"] == 30
assert program["summary"]["next_work_item_id"] == "AIA-P0-006"
assert program["summary"]["invalid_done_without_runtime_evidence_count"] == 0
def test_ai_automation_retry_rollback_terminal_fails_closed() -> None:
payload = load_latest_awoooi_priority_work_order_readback()
runtime_control = _autonomous_runtime_control_retry_rollback_closed()