a3762a4cc2
CI / lint (push) Successful in 25s
CI / security (push) Successful in 59s
CI / quality (push) Successful in 45s
CI / typecheck (push) Successful in 1m9s
CI / push-validation (push) Successful in 26s
CI / build (push) Successful in 49s
CI / helm (push) Successful in 42s
CI / e2e_tests (push) Successful in 4m18s
CI / integration_tests (push) Successful in 4m28s
CI / unit_tests (push) Successful in 5m47s
CI / docker (push) Successful in 11s
CI / coverage (push) Successful in 11m48s
CI / status-check (push) Successful in 1s
CI / benchmark-regression (push) Has been skipped
CI / benchmark-publish (push) Has been cancelled
docs: add invariant-reconciliation module guide, extend automation tracking docs, update mkdocs nav - Add docs/modules/invariant-reconciliation.md: module guide for InvariantReconciliationActor (v3.8.0) - Extend docs/development/automation-tracking.md: all 16 supervisors, centralized tracking manager - Update mkdocs.yml: add Modules nav section, Custom Sandbox Strategy to Development ISSUES CLOSED: #5700 Co-authored-by: CleverThis <hal9000@cleverthis.com> Co-committed-by: CleverThis <hal9000@cleverthis.com>
203 lines
6.5 KiB
Markdown
203 lines
6.5 KiB
Markdown
# Invariant Reconciliation Module
|
|
|
|
**Package:** `cleveragents.actor.reconciliation`
|
|
**Introduced:** v3.8.0
|
|
|
|
The Invariant Reconciliation Actor is a built-in actor that automatically runs at
|
|
every plan phase transition to ensure all active invariants are consistent and
|
|
correctly prioritised before any strategy or execution work begins.
|
|
|
|
For the invariant data model and scope hierarchy, see
|
|
[`docs/reference/invariants.md`](../reference/invariants.md).
|
|
For the `InvariantService` API, see
|
|
[`docs/api/core.md`](../api/core.md).
|
|
|
|
---
|
|
|
|
## Purpose
|
|
|
|
Invariants are natural-language constraints on plan execution that can be defined
|
|
at four scopes: global, project, action, and plan. When multiple invariants apply
|
|
to the same plan, conflicts can arise — for example, a project-level invariant may
|
|
contradict a plan-level one. The reconciliation actor resolves these conflicts
|
|
deterministically using a precedence chain and records each decision in the audit
|
|
trail.
|
|
|
|
---
|
|
|
|
## Automatic Invocation
|
|
|
|
`PlanLifecycleService` automatically invokes the reconciliation actor at the start
|
|
of three phase transitions:
|
|
|
|
| Transition | Method |
|
|
|------------|--------|
|
|
| Strategize start | `start_strategize()` |
|
|
| Execute start | `execute_plan()` |
|
|
| Apply start | `apply_plan()` |
|
|
|
|
If reconciliation fails, the phase transition is **blocked** with
|
|
`ReconciliationBlockedError` and an `INVARIANT_VIOLATED` event is emitted on the
|
|
event bus. The transition cannot proceed until the invariants are corrected.
|
|
|
|
Post-correction reconciliation runs automatically via a `CORRECTION_APPLIED` event
|
|
subscription (best-effort; does not block correction completion).
|
|
|
|
---
|
|
|
|
## Reconciliation Algorithm
|
|
|
|
The actor follows a six-step algorithm:
|
|
|
|
1. **Load** — fetch all invariants in scope for the plan (global → project → action → plan)
|
|
2. **Deduplicate** — remove exact-text duplicates, keeping the highest-scope copy
|
|
3. **Detect conflicts** — identify pairs where one invariant's constraint contradicts another's
|
|
4. **Resolve** — apply the precedence chain: `plan > action > project > global`; when scopes are equal, the invariant with the lower `position` index wins
|
|
5. **Record** — write each resolution decision to the audit trail with rationale
|
|
6. **Emit** — publish `INVARIANT_RECONCILED` event on the event bus
|
|
|
|
---
|
|
|
|
## Key Classes
|
|
|
|
### `InvariantReconciliationActor`
|
|
|
|
```python
|
|
from cleveragents.actor.reconciliation import InvariantReconciliationActor
|
|
|
|
actor = InvariantReconciliationActor(invariant_service, event_bus, audit_service)
|
|
result = await actor.reconcile(plan_id)
|
|
```
|
|
|
|
| Parameter | Type | Description |
|
|
|-----------|------|-------------|
|
|
| `invariant_service` | `InvariantService` | Provides invariant CRUD and scope queries |
|
|
| `event_bus` | `EventBus` | Receives `INVARIANT_RECONCILED` / `INVARIANT_VIOLATED` events |
|
|
| `audit_service` | `AuditService` | Records reconciliation decisions |
|
|
|
|
### `ReconciliationResult`
|
|
|
|
```python
|
|
@dataclass
|
|
class ReconciliationResult:
|
|
plan_id: str
|
|
resolved_invariants: list[Invariant]
|
|
conflicts_detected: int
|
|
conflicts_resolved: int
|
|
decisions: list[ReconciliationDecision]
|
|
success: bool
|
|
```
|
|
|
|
### `ReconciliationDecision`
|
|
|
|
```python
|
|
@dataclass
|
|
class ReconciliationDecision:
|
|
winner: Invariant
|
|
loser: Invariant
|
|
rationale: str # e.g. "plan scope > project scope"
|
|
timestamp: datetime
|
|
```
|
|
|
|
---
|
|
|
|
## Error Handling
|
|
|
|
| Exception | When raised |
|
|
|-----------|-------------|
|
|
| `ReconciliationBlockedError` | Unresolvable conflict detected; phase transition blocked |
|
|
| `InvariantLoadError` | Database error loading invariants |
|
|
| `ReconciliationTimeoutError` | Reconciliation exceeded configured timeout |
|
|
|
|
When `ReconciliationBlockedError` is raised:
|
|
|
|
1. The phase transition method re-raises the exception to the caller
|
|
2. `PlanLifecycleService` emits `INVARIANT_VIOLATED` on the event bus
|
|
3. The plan remains in its current phase (e.g., `PENDING` before Strategize)
|
|
4. The operator must correct the conflicting invariants and retry the transition
|
|
|
|
---
|
|
|
|
## DI Registration
|
|
|
|
`InvariantService` is registered as a **Singleton** in the DI container:
|
|
|
|
```python
|
|
# src/cleveragents/application/container.py (excerpt)
|
|
class Container(containers.DeclarativeContainer):
|
|
invariant_service = providers.Singleton(
|
|
InvariantService,
|
|
uow_factory=uow_factory,
|
|
event_bus=event_bus,
|
|
)
|
|
invariant_reconciliation_actor = providers.Factory(
|
|
InvariantReconciliationActor,
|
|
invariant_service=invariant_service,
|
|
event_bus=event_bus,
|
|
audit_service=audit_service,
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## Usage Examples
|
|
|
|
### Triggering Reconciliation Manually
|
|
|
|
```python
|
|
from cleveragents.application.container import Container
|
|
|
|
container = Container()
|
|
actor = container.invariant_reconciliation_actor()
|
|
result = await actor.reconcile(plan_id="plan-01JXYZ")
|
|
|
|
if not result.success:
|
|
print(f"Reconciliation failed: {result.conflicts_detected} unresolvable conflicts")
|
|
else:
|
|
print(f"Reconciled {result.conflicts_resolved} conflicts")
|
|
for decision in result.decisions:
|
|
print(f" {decision.winner.text!r} won over {decision.loser.text!r}: {decision.rationale}")
|
|
```
|
|
|
|
### Listening for Reconciliation Events
|
|
|
|
```python
|
|
from cleveragents.domain.events import INVARIANT_RECONCILED, INVARIANT_VIOLATED
|
|
|
|
@event_bus.subscribe(INVARIANT_RECONCILED)
|
|
async def on_reconciled(event):
|
|
logger.info("Invariants reconciled", plan_id=event.plan_id, decisions=event.decision_count)
|
|
|
|
@event_bus.subscribe(INVARIANT_VIOLATED)
|
|
async def on_violated(event):
|
|
logger.error("Invariant violation blocked phase transition", plan_id=event.plan_id)
|
|
# Notify operator, create alert, etc.
|
|
```
|
|
|
|
### Defining Invariants at Different Scopes
|
|
|
|
```bash
|
|
# Global invariant (applies to all plans)
|
|
agents invariant create --scope global --text "Never delete production data without a backup"
|
|
|
|
# Project-level invariant
|
|
agents invariant create --scope project --project my-project \
|
|
--text "All database migrations must be reversible"
|
|
|
|
# Plan-level invariant (highest precedence)
|
|
agents invariant create --scope plan --plan plan-01JXYZ \
|
|
--text "This plan may drop the staging schema"
|
|
```
|
|
|
|
In this example, if the plan-level invariant conflicts with the project-level one,
|
|
the plan-level invariant wins (higher precedence).
|
|
|
|
---
|
|
|
|
## Related Documentation
|
|
|
|
- [ADR-016 Invariant System](../adr/ADR-016-invariant-system.md) — design rationale
|
|
- [ADR-007 Decision Tree & Correction](../adr/ADR-007-decision-tree-and-correction.md) — correction flow
|
|
- [Core API Reference](../api/core.md) — `InvariantService` API
|
|
- [Shell Safety Module](shell-safety.md) — another built-in safety actor
|