Guards: one predicate algebra, four polarities

Everything that constrains a turn is the same kind of object: a decidable predicate over the proposed step. dregg calls this algebra Pred, and it shows up in four polarities that are usually four separate mechanisms in other systems:

One algebra, four places to stand. A predicate is either first-party (decidable right now against the old/new state) or witnessed (a registered verifier discharges it — a third-party discharge, a range proof, a zero-knowledge statement). The same predicate the executor evaluates is compiled to circuit obligations, so what you can install and what the proof enforces are one mechanism.

The constraint grammar

Cell programs are built from this constraint vocabulary — each kind a small, decidable shape over the cell's slots:

constraintfieldssemantics
FieldEquals simpleindex: u8, value: FieldField at index must equal value.
FieldGte simpleindex: u8, value: FieldField at index must be >= value (unsigned big-endian comparison).
FieldLte simpleindex: u8, value: FieldField at index must be <= value (unsigned big-endian comparison).
FieldLteFieldleft_index: u8, right_index: u8Field at left_index must be <= field at right_index in the same post-state.
FieldLteOtherindex: u8, other: u8, delta: i64Field at index must be <= field at other + delta in the same post-state (new[index] <= new[other] + delta, signed).
SumEqualsindices: Vec<u8>, value: FieldSum of fields at indices must equal value (intra-cell conservation).
WriteOnce simpleindex: u8Slot must transition only from FIELD_ZERO to any non-zero value; after the first write, the slot is frozen.
Immutable simpleindex: u8Slot value is read-only after initialization.
Monotonic simpleindex: u8new[i] >= old[i] (unsigned big-endian).
StrictMonotonic simpleindex: u8new[i] > old[i] strictly.
BoundedBy simpleindex: u8, witness_index: u8slot[index] may only be set (i.e.
FieldDeltaindex: u8, delta: Fieldnew[index] == old[index] + delta (modular field arithmetic).
FieldDeltaInRangeindex: u8, min_delta: Field, max_delta: Fieldnew[index] in [old[index] + min_delta, old[index] + max_delta].
FieldGteHeight simpleindex: u8, offset: i64new[index] >= ctx.block_height + offset.
FieldLteHeight simpleindex: u8, offset: i64new[index] <= ctx.block_height + offset.
SumEqualsAcrossinput_fields: Vec<u8>, output_fields: Vec<u8>Intra-cell conservation across the transition: sum(new[input_fields]) == sum(old[input_fields]) + sum(new[output_fields]).
SenderAuthorizedset: AuthorizedSetThe turn's sender must be in an authorized set.
CapabilityUniquenesscap_set_root_slot: u8slot[cap_set_root_slot] is a per-cell capability-set root and must encode at most one live capability of the named kind.
RateLimitmax_per_epoch: u32, epoch_duration: u64Sender may mutate this cell at most max_per_epoch times per epoch_duration blocks.
RateLimitBySumslot_index: u8, max_sum_per_epoch: u64, epoch_duration: u64Sum-based rate limit: the *value* added to slot_index over a window of epoch_duration blocks cannot exceed max_sum_per_epoch.
TemporalGate simplenot_before: Option<u64>, not_after: Option<u64>Mutation is rejected unless ctx.block_height is in [not_before, not_after].
PreimageGate simplecommitment_index: u8, hash_kind: HashKindThe action must reveal a preimage whose hash equals slot[commitment_index].
MonotonicSequenceseq_index: u8slot[seq_index] == old[seq_index] + 1.
AllowedTransitionsslot_index: u8, allowed: Vec<(Field, Field)>(old[slot_index], new[slot_index]) must appear in the explicit allow-list allowed.
TemporalPredicatewitness_index: u8, dsl_hash: Hash32Witness-attached temporal-predicate proof.
BoundDeltalocal_slot: u8, peer_cell: CellId, peer_slot: u8, delta_relation: DeltaRelationCross-cell binding pair to γ.2: this cell's local_slot delta must match peer_cell's peer_slot delta under the named DeltaRelation.
AnyOfvariants: Vec<SimpleStateConstraint>Single-level disjunction: at least one of variants must hold.
Witnessedwp: WitnessedPredicateA witness-attached predicate (DFA classification, temporal-DSL proof, blinded-set non-revocation, bridge predicate, custom AIR…).
Renouncedset: RenouncedSet**Categorical dual of SenderAuthorized: proof of non-holding / non-membership.** A *renunciation* slot caveat — the action's sender must verifiably *NOT* be in the set's sorted Merkle leaf set.
MemberOfindex: u8, set: Vec<u64>**Value allowlist:** new[index] must be one of set (the one-sided value membership the pair-table AllowedTransitions cannot express).
PrefixOfseg_indices: Vec<u8>, prefix: Vec<u64>**Namespace / path prefix containment:** the ordered scalar path read from seg_indices (each a slot read as big-endian u64) must START WITH prefix.
InRangeTwoSidedindex: u8, lo: u64, hi: u64**Two-sided absolute band:** lo <= new[index] <= hi (the ABSOLUTE counterpart to the RELATIVE FieldDeltaInRange).
DeltaBoundedindex: u8, d: u64**Real two-sided delta:** |new[index] - old[index]| <= d (symmetric; the legacy delta variants are one-sided or relative-range).
AffineLeterms: Vec<(i64, u8)>, c: i64**Affine inequality:** Σ kᵢ·new[fᵢ] <= c over named slots (terms : Vec<(i64 coefficient, u8 slot)>).
AffineEqterms: Vec<(i64, u8)>, c: i64**Affine equation:** Σ kᵢ·new[fᵢ] = c.
Reachablefrom_index: u8, to_label: u64, edges: Vec<(u64, u64)>**DAG reachability / prerequisite:** the label read from new[from_index] must reach to_label in the reachability edges (reflexive-transitive closure).
AllOfvariants: Vec<SimpleStateConstraint>**n-ary conjunction** over SimpleStateConstraints — the allOf the legacy 2-level grammar lacked (it had only single-level AnyOf).
Customir_hash: Hash32, descriptor: CustomDescriptor, reads: ReadSetDSL-authored predicate.
SenderIs simplepk: Hash32The turn's sender (acting cell's public key) must equal pk.
SenderInSlot simpleindex: u8The turn's sender must equal the identity held in new[index].
BalanceGte simplemin: u64The cell's own post-turn balance must be >= min.
BalanceLte simplemax: u64The cell's own post-turn balance must be <= max.
KeyRotationGatedigest_slot: u8, current_slot: u8, last_rotated_slot: u8, cooling_period: u64, hash_kind: HashKind**Pre-rotation gate (KERI-shaped)** — the identity rider (docs/ORGANS.md "Identity rider"; kernel semantics proven in metatheory/Dregg2/Apps/PreRotation.lean, the rotateWriteCooled production shape).
HeapField simplekey: u64, atom: HeapAtom**Heap-keyed atom (THE ROTATION's app-state lane)** — the top-level lift of HeapAtom over heap key key, the twin of HeapField (both surfaces share ONE evaluator arm, like the SenderIs/BalanceGte precedent).
DelegationEpochEquals simpleindex: u8new[index] must equal the touched cell's post-turn delegation_epoch.
CountGe simplethreshold: u32, set_commitment_slot: u8The witness must exhibit ≥ threshold distinct elements opening the commitment in new[set_commitment_slot].
SenderMemberOf simplemembers: Vec<Hash32>The turn's sender must be one of members.
BalanceDeltaLte simplemax: i64The cell's per-turn balance change is <= max.
BalanceDeltaGte simplemin: i64The cell's per-turn balance change is >= min.
AffineDeltaLeterms: Vec<(i64, u8)>, c: i64**Multi-field delta gate:** Σ kᵢ·(new[fᵢ] − old[fᵢ]) <= c over named slots (terms : Vec<(i64 coefficient, u8 slot)>).
ObservedFieldEqualslocal_field: u8, source_cell: Hash32, source_field: u8, at_root: Hash32, proof_witness_index: usize**Cross-cell verified observation** (docs/CELL-PROGRAM-LANGUAGE.md §11.2): new[local_field] must equal the value source_field held by the PEER cell source_cell at the FINALIZED state-commitment root at_root.
CollectionAggregatecollection_id: u32, stride: u32, fuel: u32, pred: CollPred**Aggregate-over-a-collection gate** (docs/CELL-PROGRAM-LANGUAGE.md gaps 7/11.1 — the heap/layout rung, the documented "lamesauce" N≤3 fixed-slot cap *lifted*).
AnyOfBoundbranches: Vec<BoundBranch>**Witnessed branches under disjunction** (docs/CELL-PROGRAM-LANGUAGE.md §11.3 — the AnyOfBound rung) — admits IFF SOME branches element admits.
SymEqindex: u8, sym: u64**SymEq** — field_to_u64(new[index]) == sym: the field's interned identity (the u64 lane) equals sym.
SymMemberOfindex: u8, set: Vec<u64>**SymMemberOf** — field_to_u64(new[index]) ∈ set: enum membership by the symbol lane ("status ∈ {Draft, Active, Frozen}").
DigEqindex: u8, digest: Field**DigEq** — new[index] == digest as a FULL 32-byte field: the field's digest / cell-reference equals digest.
DigFieldEqleft_index: u8, right_index: u8**DigFieldEq** — new[left_index] == new[right_index] as FULL 32-byte fields: two digest slots are equal.
57 constraint kinds, generated from cell/src/program.rs (the doc-commented canonical enum), cross-checked against the JSON projection the studio renders.

Beyond single-slot shapes, the relational closure admits any affine relation over the post-state (head ≤ tail + capacity, Σ slots = const) as the same Pred object — no new atom per shape (Authority/RelationalClosure.lean, e.g. ofFieldLteOther_eq). Bounded quantifiers compile down to the same closure with a proven constraint budget (QuantifiedPredicate.compile_sound, andFold_budget).

The coordination dial

Guards carry a price tag, and the system computes it rather than hiding it. Two concurrent turns can merge without coordination exactly when their guards are confluence-stable — the invariant survives merging independently-taken steps. A guard that is not confluence-stable forces ordering: it is the system telling you that what you asked for costs consensus. The classifier is a theorem, not advice:

The disclosure dial

The second dial is how much a guard reveals while being checked. The principle: what the proof does not need, it does not ask to see. The ladder, from most to least disclosed:

Grounding

Touch it: compose and evaluate predicates live in the ontology page's predicate workbench, or install a program on a cell in the Studio and watch the executor reject a violating turn.

← 2 · Cells & substances · 4 · Receipts & Q →