vera-effects 0.1.1

VERA (Verified Effect-Rule Architecture) — generic trace, rule output, and reaction types for building verifiable game loops where every state mutation is inspectable and testable.
Documentation
  • Coverage
  • 52.38%
    11 out of 21 items documented1 out of 9 items with examples
  • Size
  • Source code size: 28.91 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 2.37 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 36s Average build duration of successful builds.
  • all releases: 24s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • EliasVahlberg/vera-effects
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • EliasVahlberg

vera-effects

VERA — Verified Effect-Rule Architecture

An architectural pattern and supporting types for game loops where every state mutation is inspectable, testable, and traceable. Designed for codebases developed with AI coding agents, where self-verification is the primary constraint.

The problem

In a typical game loop, actions mutate state directly:

// Traditional: opaque mutation
fn use_item(&mut self, idx: usize) {
    let def = get_item_def(&self.inventory[idx]);
    self.hp += def.heal;                    // What changed?
    self.inventory.remove(idx);             // Why?
    self.ap -= 1;                           // Was this correct?
    self.refraction -= def.reduces_refraction; // How do we test this?
    if def.reveals_map { self.reveal_all(); }  // Can't unit test without full game state
}

When this breaks, you debug the entire game state. When an AI agent writes this, there's no structural gate between "compiles" and "works." The scaffold-and-abandon pattern — code that exists but isn't wired into gameplay — is invisible until a player hits it.

The VERA pattern

VERA separates deciding what should change from applying the change from recording what happened:

// VERA: inspectable, testable, traceable

// 1. RULE — pure function, no mutation, returns a description of what should change
fn rule_use_item(idx: usize, ctx: &QueryContext) -> RuleOutput<Effect, Presentation> {
    let def = ctx.item_def(&ctx.player.inventory[idx]);
    let mut effects = vec![
        Effect::Player(PlayerEffect::SpendAp { amount: 1 }),
        Effect::Item(ItemEffect::Consume { item_id: def.id.clone(), index: idx }),
    ];
    if def.heal > 0 {
        effects.push(Effect::Player(PlayerEffect::Heal {
            amount: def.heal.min(ctx.player.max_hp - ctx.player.hp),
        }));
    }
    if def.reveals_map {
        effects.push(Effect::Map(MapEffect::RevealAll));
    }
    RuleOutput { effects, presentation: vec![] }
}

// 2. APPLY — mechanical match, no logic, just field assignments
fn apply_effect(state: &mut GameState, effect: &Effect) {
    match effect {
        Effect::Player(PlayerEffect::Heal { amount }) => {
            state.hp = (state.hp + amount).min(state.max_hp);
        }
        Effect::Player(PlayerEffect::SpendAp { amount }) => {
            state.ap -= amount;
        }
        // ... exhaustive match — compiler catches unhandled variants
    }
}

// 3. TRACE — records what happened for test assertions
for effect in &output.effects {
    apply_effect(state, effect);
    trace.record(effect, TraceSource::Rule { name: "rule_use_item" }, turn);
}

What this gives you

Without VERA With VERA
Test requires full game state setup Test requires only a QueryContext (borrowed refs)
Debugging means tracing call chains Debugging means reading the trace log
"Does this feature work?" → play the game "Does this feature work?" → assert on effects
New effect variant compiles silently New effect variant → compiler error if unhandled
AI agent writes code that compiles but isn't wired AI agent must produce effects or tests fail

The three verification layers

  1. Rule unit tests — pure input→output. No game state needed. Assert on the Vec<Effect> returned.
  2. Integration tests — drive the full loop (command → rule → apply → state). Assert on final state AND on the trace.
  3. Compiler — exhaustive match on Effect and Command enums. Unhandled variants are compile errors.

What this crate provides

The generic infrastructure types. Your game defines the concrete Effect/Command enums.

use vera_effects::{Trace, TraceEntry, TraceSource, RuleOutput};

// Specialize for your game's types
type GameTrace = Trace<MyEffect>;
type GameRuleOutput = RuleOutput<MyEffect, MyPresentation>;

Types

Type Purpose
Trace<E> Ordered record of effects. Enabled during tests, zero-cost when disabled.
TraceEntry<E> Single entry: effect + source + turn number
TraceSource<E> Where an effect came from: Rule { name } or Reaction { name, trigger }
RuleOutput<E, P> What a rule returns: game effects (traced) + presentation effects (not traced)

Trace API

let mut trace = Trace::<Effect>::default();
trace.enabled = true;

// Record effects
trace.record(&effect, TraceSource::Rule { name: "rule_heal" }, turn);

// Query
trace.contains(&Effect::Heal { amount: 25 });       // did this happen?
trace.from_rule("rule_heal");                         // all effects from this rule
trace.effects_matching(|e| matches!(e, Effect::Heal { .. })); // filter by pattern

The full pattern (not in this crate)

This crate provides the generic types. The full VERA pattern also includes:

  • Commands — enum of player/system intentions (your game defines these)
  • Effects — domain-scoped enums describing state mutations (your game defines these)
  • Rules — pure functions: (args, &Context, &mut RNG) → RuleOutput<E, P>
  • Apply — mechanical match on effects → field assignments
  • Reactions — functions triggered by effects: (effect, &Context, &mut RNG) → Vec<Effect>
  • QueryContext — borrowed read-only view of game state for rules
  • TestContext — builder for constructing QueryContext in unit tests without full game state

These are domain-specific and live in your game crate, not here.

When to use VERA

VERA is designed for:

  • Turn-based games where actions produce discrete state changes
  • Codebases developed with AI coding agents that need self-verification
  • Projects where "it compiles" is not sufficient proof that a feature works

VERA is not designed for:

  • Real-time physics simulations (continuous state, not discrete effects)
  • Procedural generation pipelines (data transformation, not command→effect loops)
  • Rendering (read-only, no state mutation)

License

MIT