Council Orchestration Model
iris intermediate 5 min read
ELI5
A council is like a project team with a strict org chart. Everyone has a role, there’s one designated person who can say “no” to anything (the gate authority), and there are written rules about what the team can and cannot do. When work comes in, the team follows a predefined playbook (chain) step by step.
Technical Deep Dive
Council JSON Schema
A Council is the second top-level type in iris.schema.json (identified by the presence of sprites).
Required Fields
| Field | Type | Constraints |
|---|---|---|
id | UUID | Server-generated |
name | string | Human-readable council name |
domain | string | Unique namespace for this council (e.g., engineering, security, content) |
sprites | Sprite[] | minItems: 1. Full Sprite objects embedded, not just UUID references |
chains | Chain[] | Array of Chain definitions belonging to this council |
gate_agents | UUID[] | maxItems: 1. Exactly one sprite holds gate authority |
rules | Rule[] | Governance rules evaluated in order; first match determines action |
Rule Evaluation
Rules are evaluated sequentially at council creation and during chain execution:
flowchart TD A["Rule 1: condition matches?"] -->|Yes| B{"Action?"} A -->|No| C["Rule 2: condition matches?"] C -->|Yes| D{"Action?"} C -->|No| E["Rule 3..."] B -->|allow| F["Proceed ✓"] B -->|deny| G["Block ✗"] B -->|escalate| H["Escalate ↑"] D -->|allow| F D -->|deny| G D -->|escalate| H E -->|No match| I["Default: allow"]Rule Structure
| Field | Type | Description |
|---|---|---|
id | string | Unique rule identifier |
name | string | Human-readable rule name |
condition | string | YAML-formatted expression, up to 4,096 chars. Supports sprite references and chain state |
action | enum | allow, deny, or escalate |
hook | string (opt) | Notification/logging/webhook trigger (e.g., notify:slack:#iris-alerts, webhook:https://ops.iris.dev/escalate) |
Enforcement Levels (from OpenAPI contract)
| Level | Behaviour | Example |
|---|---|---|
| Mandatory | Halts execution on violation | ”All code changes must be reviewed by BECK-02” |
| Advisory | Logs a warning but allows continuation | ”Consider adding tests for new capabilities” |
Gate Authority
stateDiagram-v2 [*] --> Active: Council created Active --> Vetoed: Gate evaluates condition → veto Active --> Allowed: Gate evaluates condition → allow Vetoed --> Active: Council reconfigured Allowed --> Active: Next chain step Active --> [*]: Council dissolvedEach council designates exactly one sprite as gate authority. This sprite:
- Has veto power over chain execution at
before,after, andon_errorcheckpoints - Cannot be bypassed if
protected: true - Gate decisions are logged and fully auditable
Class Diagram
classDiagram class Council { +UUID id +string name +string domain +Sprite[] sprites +Chain[] chains +UUID[] gate_agents +Rule[] rules +datetime created_at +validate_structure() boolean +execute_chain(chain_name) ChainExecutionResult +execute_all_chains() ChainExecutionResult[] } class Rule { +string id +string name +string condition +Action action +string hook } class Chain { +UUID id +string name +ChainStep[] steps +Gate[] gates +Duration timeout } class Sprite { +UUID id +SpriteName name +boolean gate_authority +boolean protected } Council --> Sprite : contains Council --> Chain : owns Council --> Rule : governsValidation Chain (iris-service)
When creating a council via POST /v1/councils, the endpoint enforces:
- Domain must not be empty → 400
- Sprites list must not be empty → 400
- Gate agents list must not be empty → 400
- All sprite IDs must exist in registry → 404 (with missing IDs)
- All gate agent IDs must exist in registry → 404
- Gate agents must be subset of sprites → 400
INVALID_GATE_AGENT - Exactly one gate agent required (DEC-002) → 400
INVALID_GATE_AGENT - Domain must be unique → 409
COUNCIL_CONFLICT
Key Terms
- Domain → A unique namespace identifier for a council (e.g.,
engineering). No two councils can share a domain. - Gate agent → The single sprite per council with veto authority. Max 1 per council (DEC-002).
- Rule → A governance condition-action pair. Evaluated sequentially; first match wins.
- Mandatory rule → Halts execution on violation (hard constraint)
- Advisory rule → Logs warning but allows continuation (soft constraint)
- Hook → Optional notification trigger attached to a rule (
notify:slack:...,webhook:...) - Protected sprite → Cannot be bypassed in chain execution; requires force flag for deletion
Q&A
Q: Why only one gate agent per council? A: DEC-002 mandates exactly one gate authority to prevent ambiguity during veto decisions. Multiple gate agents could deadlock or produce conflicting decisions. If consensus is needed, model it as a single sprite whose internal logic aggregates multiple inputs.
Q: What happens if no rules match?
A: Default action is allow. The council proceeds with chain execution unless a gate vetoes.
Q: Can a sprite be in multiple councils? A: Yes. Sprites are referenced by ID across councils. A single sprite (e.g., a reviewer) can serve multiple domains.
Q: Are rules re-evaluated during chain execution or only at creation?
A: Rules are enforced at council creation (structural validation). Gate evaluation happens dynamically during chain execution based on the gate’s condition and current context.
Examples
A council is like a hospital surgery team:
- Domain = The operating theatre (e.g., “Cardiology Suite 3”)
- Sprites = The surgeon, anaesthetist, scrub nurse, circulating nurse
- Gate agent = The senior surgeon who can say “stop the operation” if something goes wrong
- Rules = Hospital protocols (mandatory: “verify patient identity before incision”; advisory: “consider music volume below 60dB”)
- Chains = The surgical procedure: step 1 (anaesthesia), step 2 (incision), step 3 (repair), step 4 (closure)
- Protected sprite = The anaesthetist — you absolutely cannot skip them
neighbors on the map
- Chain & ChainStep Schema designing a new chain
- GRACE Session Structure & Context Lifecycle starting a new GRACE session