docs: add invariant-reconciliation module guide, extend automation tracking docs, update mkdocs nav
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
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>
This commit was merged in pull request #5614.
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user