Compare commits

...

1 Commits

Author SHA1 Message Date
HAL9000 5b741ee1bb docs(spec): document ReconciliationBlockedError and invariant reconciliation failure behavior
CI / push-validation (pull_request) Successful in 21s
CI / helm (pull_request) Successful in 24s
CI / build (pull_request) Successful in 24s
CI / lint (pull_request) Successful in 26s
CI / quality (pull_request) Successful in 45s
CI / e2e_tests (pull_request) Successful in 3m15s
CI / typecheck (pull_request) Successful in 3m58s
CI / security (pull_request) Successful in 4m8s
CI / integration_tests (pull_request) Successful in 6m51s
CI / unit_tests (pull_request) Successful in 9m50s
CI / docker (pull_request) Successful in 11s
CI / coverage (pull_request) Successful in 13m33s
CI / status-check (pull_request) Successful in 1s
CI / benchmark-publish (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Successful in 57m26s
The spec described invariant reconciliation as happening 'during Strategize'
only. The implementation (PR #5614) runs reconciliation at all three phase
transitions: Strategize, Execute, and Apply.

Added §Reconciliation Failure Behavior documenting:
- Reconciliation runs at start_strategize(), execute_plan(), apply_plan()
- ReconciliationBlockedError is raised when reconciliation fails
- INVARIANT_VIOLATED event is emitted on the event bus
- Resolution path: plan correct + retry
- Post-correction reconciliation via CORRECTION_APPLIED event subscription
- Built-in actor: builtin/invariant-reconciliation

Closes #5942
2026-04-13 05:46:23 +00:00
+32
View File
@@ -19750,6 +19750,38 @@ Each effective invariant is then recorded as an `invariant_enforced` decision in
**Child plan inheritance**: When a top-level plan spawns child plans, the parent's effective invariant view (already reconciled) is passed down to each child plan. Child plans do not re-run reconciliation — they inherit the parent's resolved view.
#### Reconciliation Failure Behavior { #reconciliation-failure }
Invariant reconciliation runs at the start of **three phase transitions** — not just Strategize:
| Phase Transition | Method | When Reconciliation Runs |
|-----------------|--------|--------------------------|
| Strategize start | `start_strategize()` | Before strategy actor is invoked |
| Execute start | `execute_plan()` | Before execution actor is invoked |
| Apply start | `apply_plan()` | Before apply merge is performed |
When reconciliation fails at any phase transition, the transition is **blocked** — the plan cannot proceed to the next phase. The system raises `ReconciliationBlockedError` and emits an `INVARIANT_VIOLATED` event on the event bus. The plan remains in its current phase state (e.g., `strategize/blocked`) until the invariant conflict is resolved.
**Resolution path**: Use `agents plan correct <DECISION_ID> --mode=revert` to remove or replace the conflicting invariant decision, then retry the phase transition. After a correction is applied, reconciliation automatically re-runs via a `CORRECTION_APPLIED` event subscription (best-effort; does not block correction completion).
**Built-in actor**: The default reconciliation actor is registered as `builtin/invariant-reconciliation`. It can be overridden at the plan, project, or global config level via `--invariant-actor`.
```python
# ReconciliationBlockedError is raised when reconciliation fails at a phase transition
class ReconciliationBlockedError(CleverAgentsError):
"""Raised when invariant reconciliation blocks a phase transition.
Attributes:
plan_id: The plan whose phase transition was blocked.
phase: The phase transition that was blocked ('strategize', 'execute', 'apply').
conflicts: List of invariant conflicts that caused the block.
event: The INVARIANT_VIOLATED event emitted on the event bus.
"""
plan_id: str
phase: str
conflicts: list[InvariantConflict]
```
**Correcting invariants**: The correction mechanism for `invariant_enforced` decisions supports two operations:
* **Remove**: Remove an existing invariant from the plan's decision tree (the invariant remains defined at its scope but is no longer enforced for this plan).
* **Add**: Add a new invariant to the plan. When adding, the user can select from invariants already accessible to the plan (those defined at the plan, action, project, or global scope), or provide free-form text to create a new ad-hoc invariant.