8.6 KiB
adr_number, title, status_history, tier, authors, superseded_by, related_adrs, acceptance
| adr_number | title | status_history | tier | authors | superseded_by | related_adrs | acceptance | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 16 | Invariant System |
|
3 |
|
null |
|
|
Context
When AI systems make autonomous decisions, they must operate within defined boundaries. A strategy actor choosing an implementation approach must respect project-wide coding standards. An execution actor modifying files must respect API compatibility requirements. These constraints — invariants — may be defined at different scopes (global, project, action, plan) and may conflict with each other. The system needs a structured mechanism for declaring, scoping, resolving conflicts, and enforcing invariants across the plan lifecycle.
Decision Drivers
- Autonomous AI decisions must operate within declared boundaries (coding standards, API compatibility, etc.)
- Constraints originate at different scopes — system-wide standards vs. project-specific rules vs. plan-specific overrides
- Conflicting invariants across scopes need a deterministic, predictable resolution mechanism
- Invariants must be visible in the decision tree so they can be corrected, audited, and visualized like any other decision
- Child plans must inherit parent constraints without manual propagation
Decision
CleverAgents implements an invariant system with four scopes (global, project, action, plan), a precedence-based resolution chain, an Invariant Reconciliation Actor that resolves conflicts, and explicit recording of enforced invariants as invariant_enforced decision nodes in the plan's decision tree. Invariants flow through the hierarchy — child plans inherit the effective invariant set of their parent.
Design
Four Scopes
Global invariants: Apply to all plans in the system. Set via agents invariant add --global. Example: "All code must pass Ruff linting."
Project invariants: Apply to all plans targeting a specific project. Set via agents invariant add --project <PROJECT>. Example: "Maintain API compatibility with v2 callers."
Action invariants: Attached to an action template and carried forward to every plan spawned from that action. Set in the action YAML or via agents invariant add --action <ACTION>. Example: "Do not modify the public interface of module X."
Plan invariants: Apply to a specific plan instance. Set via --invariant flag at plan use time or via agents invariant add --plan <PLAN>. Example: "Use only async patterns in this refactoring."
Precedence Rules
When invariants conflict, the more specific scope wins:
plan > action > project > global
If an invariant at a lower scope contradicts one at a higher scope, the lower (more specific) scope takes precedence. This enables plans to override project-level constraints when justified.
Invariant Reconciliation Actor
At the entry to the Strategize phase, the Invariant Reconciliation Actor computes the effective invariant view:
- Collects invariants from all four scopes.
- Identifies conflicts (contradictory invariants across scopes).
- Resolves conflicts using the precedence rules.
- Records each enforced invariant as an
invariant_enforceddecision in the plan's decision tree.
The Invariant Reconciliation Actor is itself an actor, configurable at the plan, action, project, or global level (lookup chain: plan → action → project → actor.default.invariant).
Decision Tree Integration
Enforced invariants appear as invariant_enforced decision nodes in the plan's decision tree. This means:
- Invariants are visible in the decision tree visualization.
- Invariants constrain downstream decisions (strategy choices, resource selections, child plan blueprints).
- Invariants can be corrected using the standard
agents plan correctcommand (e.g., removing an inappropriate invariant). - Child plans inherit the effective invariant set of their parent.
Child Plan Inheritance
When a parent plan spawns child plans, the child inherits the parent's effective invariant set. Additional invariants can be added at the child plan level. The child's Invariant Reconciliation Actor reconciles the inherited set with any child-specific additions.
Correction Mechanism
Invariants are correctable like any other decision:
- To remove an inappropriate invariant: correct the
invariant_enforceddecision. - To add a missing invariant: use
agents invariant add --plan <PLAN>and re-enter Strategize. - Correcting an invariant invalidates all downstream decisions that were constrained by it.
Constraints
- Every plan must have its effective invariant view computed at Strategize entry. Plans must not enter Strategize without invariant reconciliation.
- Invariant precedence (plan > action > project > global) is fixed and cannot be overridden.
- Invariants are recorded as decision tree nodes. They must not exist as side-channel metadata invisible to the decision tree.
- Child plans must inherit the parent's effective invariant set. Dropping inherited invariants requires explicit correction.
- The Invariant Reconciliation Actor must be deterministic for a given set of inputs — same invariants, same conflict resolution, same output.
Consequences
Positive
- Four scopes provide fine-grained control — from system-wide standards to plan-specific constraints.
- Recording invariants as decision tree nodes enables unified correction, visualization, and auditability.
- Precedence-based resolution provides a clear, predictable conflict resolution mechanism.
- Child plan inheritance ensures that constraints flow through the entire plan hierarchy without manual propagation.
Negative
- Four scopes create complexity in understanding which invariants are effective for a given plan.
- The Invariant Reconciliation Actor adds a processing step at Strategize entry.
- Invariant conflicts may be subtle and difficult for users to diagnose without good visualization tooling.
Risks
- Overly restrictive invariants (especially global ones) could prevent plans from completing, requiring manual intervention to relax constraints.
- The precedence chain may produce surprising results when plan-level invariants override well-intentioned project-level safety constraints.
- Stale invariants (referencing deprecated APIs, removed modules, etc.) could cause unnecessary failures.
Alternatives Considered
Single-scope invariants (global only) — Too coarse. Different projects have different requirements, and individual plans may need to temporarily override project standards (e.g., a migration plan that deliberately breaks API compatibility).
Invariants as metadata outside the decision tree — Would create a parallel constraint system invisible to the correction mechanism. Recording invariants as decision nodes ensures they are subject to the same correction, visualization, and auditing as all other decisions.
Compliance
- Scope precedence tests: Unit tests verify that invariant resolution correctly applies plan > action > project > global precedence in all conflict scenarios.
- Decision tree integration tests: Tests verify that enforced invariants appear as
invariant_enforcednodes and correctly constrain downstream decisions. - Inheritance tests: Tests verify that child plans inherit the parent's effective invariant set and that child-level additions are correctly reconciled.
- Correction tests: BDD scenarios verify that correcting an invariant invalidates downstream decisions and that adding/removing invariants triggers re-strategization.
- Reconciliation determinism: Tests verify that the Invariant Reconciliation Actor produces identical output for identical inputs.