Files
cleveragents-core/docs/reference/invariants.md
T
freemo 2d4b330f75
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Failing after 14s
CI / build (pull_request) Successful in 16s
CI / quality (pull_request) Successful in 17s
CI / typecheck (pull_request) Successful in 30s
CI / coverage (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Has been skipped
CI / security (pull_request) Successful in 33s
CI / integration_tests (pull_request) Successful in 2m49s
CI / unit_tests (pull_request) Successful in 5m25s
CI / docker (pull_request) Has been skipped
feat(invariant): add invariant models and enforcement
2026-02-22 09:23:35 +00:00

135 lines
4.3 KiB
Markdown

# Invariants
Invariants are natural-language constraints on plan execution. They flow into the
decision tree during Strategize and are reconciled by the Invariant Reconciliation
Actor before any strategy work begins.
## Scope Hierarchy
| Scope | Description |
|-----------|----------------------------------------------------------|
| `GLOBAL` | Applies to every plan in the system |
| `PROJECT` | Applies to plans targeting a specific project |
| `ACTION` | Defined in an action template; promoted on `plan use` |
| `PLAN` | Attached directly to a specific plan |
## Merge Precedence
When computing the effective set of invariants for a plan, the precedence chain is:
```
plan > project > global
```
- **Plan-level** invariants take highest precedence.
- **Project-level** invariants apply next.
- **Global-level** invariants are lowest precedence.
- **Action-level** invariants are promoted to plan scope when `plan use` is called.
### De-duplication
Invariants are de-duplicated by text (case-insensitive). When the same constraint
text appears at multiple scopes, only the highest-precedence copy is kept.
### Merge Example
Given:
- Global: `"Never delete production data"`, `"Log all changes"`
- Project (`myapp`): `"All API changes need tests"`, `"Log all changes"`
- Plan: `"Use Python 3.13 only"`
The effective set for a plan targeting `myapp` would be:
1. `"Use Python 3.13 only"` (plan)
2. `"All API changes need tests"` (project)
3. `"Log all changes"` (project — shadows the global duplicate)
4. `"Never delete production data"` (global)
## Enforcement
At the start of the Strategize phase, the Invariant Reconciliation Actor:
1. Collects the effective invariant set for the plan.
2. Evaluates each invariant against the current plan context.
3. Creates an `InvariantEnforcementRecord` for each invariant.
4. Records an `invariant_enforced` decision in the decision tree.
### Enforcement Record
Each record contains:
| Field | Description |
|------------------|--------------------------------------------------|
| `invariant_id` | ULID of the invariant |
| `enforced` | Whether the invariant was successfully enforced |
| `actor_response` | Response text from the reconciliation actor |
| `decision_id` | ULID of the associated decision node |
## Violation Model
When an invariant is violated during execution, an `InvariantViolation` is created:
| Field | Description |
|-----------------|---------------------------------------------------|
| `invariant_id` | ULID of the violated invariant |
| `violated_text` | The invariant text that was violated |
| `severity` | `error`, `warning`, or `info` |
| `details` | Additional violation context |
## CLI Usage
### Add an invariant
```bash
# Global invariant
agents invariant add --global "Never delete production data"
# Project-scoped invariant
agents invariant add --project myapp "All API changes need tests"
# Plan-specific invariant
agents invariant add --plan 01HXYZ... "Use Python 3.13 only"
# Action-scoped invariant
agents invariant add --action local/code-coverage "Minimum 80% coverage"
```
### List invariants
```bash
# List all active invariants
agents invariant list
# Filter by scope
agents invariant list --global
agents invariant list --project myapp
# Show merged effective set for a project
agents invariant list --effective --project myapp
# Filter by regex
agents invariant list "data.*safe"
# JSON output
agents invariant list --format json
```
### Remove an invariant
```bash
# With confirmation prompt
agents invariant remove 01HXYZ...
# Skip confirmation
agents invariant remove --yes 01HXYZ...
```
## Relationship with PlanInvariant
The existing `PlanInvariant` model in `plan.py` represents invariants already
attached to a plan with an `InvariantSource` provenance tag. The new `Invariant`
model in `invariant.py` is the standalone storage model used by `InvariantService`
for cross-scope management. When a plan is created via `plan use`, action
invariants are converted to `PlanInvariant` entries on the plan itself.