From 5b741ee1bb469f7cb0e018e97e3f6b02fc0e2d80 Mon Sep 17 00:00:00 2001 From: CleverThis Date: Sun, 12 Apr 2026 08:01:52 +0000 Subject: [PATCH] docs(spec): document ReconciliationBlockedError and invariant reconciliation failure behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- docs/specification.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/specification.md b/docs/specification.md index f0803345d..f48f5284d 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -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 --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. -- 2.52.0