Snapshot
Sources: docs-original/GLOSSARY.md, docs-original/ARCHITECTURE.md, packages/core/docs/SPEC.md Status: Core Concept
What is Snapshot?
Definition: The complete state of a system at a point in time. Snapshot is the single source of truth and the only communication channel between Core and Host.
Canonical Principle:
All communication happens through Snapshot. There is no other channel.
If it's not in Snapshot, it doesn't exist.
Structure
type Snapshot<TData = unknown> = {
/** Domain data (matches StateSpec) */
readonly data: TData;
/** Computed values (matches ComputedSpec) */
readonly computed: Record<SemanticPath, unknown>;
/** System state */
readonly system: SystemState;
/** Input for current action (if any) */
readonly input: unknown;
/** Snapshot metadata */
readonly meta: SnapshotMeta;
};
type SystemState = {
/** Current status */
readonly status: 'idle' | 'computing' | 'pending' | 'error';
/** Last error (null if none) */
readonly lastError: ErrorValue | null;
/** Error history */
readonly errors: readonly ErrorValue[];
/** Pending requirements waiting for Host */
readonly pendingRequirements: readonly Requirement[];
/** Current action being processed (if any) */
readonly currentAction: string | null;
};
type SnapshotMeta = {
/** Monotonically increasing version */
readonly version: number;
/** Timestamp of last modification */
readonly timestamp: number;
/** Deterministic random seed from Host context */
readonly randomSeed: string;
/** Hash of the schema this snapshot conforms to */
readonly schemaHash: string;
};The Five Sections
1. data
- Contains all domain state
- Matches the structure defined in StateSpec
- Directly modified by patches
- The "business state" of your application
snapshot.data = {
todos: [
{ id: "1", title: "Buy milk", completed: false },
{ id: "2", title: "Write code", completed: true }
],
filter: "all"
}2. computed
- Derived values calculated from data
- Never stored, always recalculated
- Defined in ComputedSpec
- Form a Directed Acyclic Graph (DAG)
snapshot.computed = {
activeCount: 1, // Computed from todos
completedCount: 1, // Computed from todos
canClearCompleted: true // Computed from completedCount
}3. system
- Runtime state managed by Core and Host
- Status of current computation
- Pending requirements (effects waiting to execute)
- Errors encountered during execution
snapshot.system = {
status: 'pending',
lastError: null,
errors: [],
pendingRequirements: [
{ id: 'req-1', type: 'api:createTodo', params: {...} }
],
currentAction: 'addTodo'
}4. input
- Transient input for the current action
- Provided by the Intent
- Available to Flow via
expr.input()
snapshot.input = {
title: "Buy groceries",
priority: "high"
}5. meta
- Metadata about the Snapshot itself
- Version (monotonically increasing)
- Timestamp (when last modified)
- SchemaHash (which schema this conforms to)
snapshot.meta = {
version: 42,
timestamp: 1704067200000,
randomSeed: "seed",
schemaHash: "sha256:abc..."
}Core Invariants
Immutability
Snapshots are immutable after creation. Any change creates a new Snapshot.
// FORBIDDEN
snapshot.data.count = 5; // Direct mutation!
// REQUIRED
const context = { now: 0, randomSeed: "seed" };
const newSnapshot = core.apply(schema, snapshot, [
{ op: 'set', path: 'count', value: 5 }
], context);Version Monotonicity
Version MUST increase by exactly 1 per apply() call.
// Initial snapshot
snapshot.meta.version = 0
// After one patch
newSnapshot.meta.version = 1
// After another patch
newerSnapshot.meta.version = 2Computed Values Are Never Stored
Computed values are recalculated every time Snapshot is accessed. They are not persisted.
// When Snapshot is loaded from storage:
const loaded = JSON.parse(storage);
// loaded.computed is empty or stale
// Recompute computed values when needed:
import { evaluateComputed } from "@manifesto-ai/core";
const result = evaluateComputed(schema, loaded);
if (result.ok) {
const fresh = { ...loaded, computed: result.value };
// fresh.computed is now up-to-date
}Why Snapshot is the Only Medium
Problem: Hidden State Channels
Traditional systems have multiple communication channels:
// Traditional (multiple channels)
const result = await executeEffect(); // Value channel
core.resume(result); // Hidden continuation
context.set('temp', result); // Context channel
eventBus.emit('done', result); // Event channelProblems:
- Hard to trace where values come from
- Non-deterministic (order-dependent)
- Cannot serialize complete state
- Cannot reproduce computation
Solution: Single Channel
Manifesto has one and only one channel:
// Manifesto (single channel)
const patches = await executeEffect();
const context = { now: 0, randomSeed: "seed" };
snapshot = core.apply(schema, snapshot, patches, context);
await core.compute(schema, snapshot, intent, context);Benefits:
- Complete state visibility
- Deterministic (same input → same output)
- Serializable (entire world can be saved)
- Reproducible (replay by re-applying patches)
Common Misconceptions
Misconception 1: "Computed is cached"
Wrong: Computed values might be cached for performance.
Right: Computed values are always recalculated. There is no caching guarantee.
If you need caching, store the result in data.
Misconception 2: "Snapshot is just state"
Wrong: Snapshot is like Redux state.
Right: Snapshot includes:
- Domain state (data)
- Derived values (computed)
- Runtime state (system)
- Transient input (input)
- Metadata (meta)
It's the complete picture, not just business state.
Misconception 3: "I can pass values outside Snapshot"
Wrong: Effect handlers can return values to Flows.
Right: Effect handlers return patches. The next compute() reads the result from Snapshot.
// WRONG
async function handler() {
const result = await api.call();
return result; // Where does this go?
}
// RIGHT
async function handler() {
const result = await api.call();
return [{
op: 'set',
path: 'apiResult',
value: result
}];
}Snapshot in the Computation Cycle
┌─────────────────────────────────────────┐
│ Snapshot₀ (initial) │
│ data: { count: 0 } │
└────────────────┬────────────────────────┘
│ Intent: "increment"
▼
┌─────────────────────┐
│ Core.compute() │
└─────────┬───────────┘
│ Returns: patches
▼
┌─────────────────────────────────────────┐
│ Snapshot₁ (after apply) │
│ data: { count: 1 } │
│ meta.version: 1 │
└─────────────────────────────────────────┘Requirements
From SPEC.md:
- Snapshots MUST be immutable
versionMUST be incremented on every changecomputedMUST be consistent withdata(no stale values)- All communication between Host and Core happens through Snapshot
Related Concepts
- Patch - Atomic mutation instruction
- World - Immutable record wrapping Snapshot with governance metadata
- Requirement - Effect declaration stored in
system.pendingRequirements - ComputedSpec - Defines how computed values are derived
See Also
- Schema Specification - Normative specification
- Core FDR - Design rationale including Snapshot as Only Medium
- Data Flow - How Snapshot moves through layers
- Effect - How effects modify Snapshot