Skip to content

Execution — compiler and runtime

These two crates are half the codebase and the part that is hardest to hold in your head: the compiler turns a Graph into an ExecutionPlan, and the runtime walks that plan. Everything else in the workspace is either vocabulary (Foundation) or a caller.

The notation legend applies throughout. (!) marks a documented deviation, with the entry in the Debt Register.


Turn a Graph into an ExecutionPlan and say what is wrong with it while doing so. It resolves cache state, validates schemas between connected nodes, claims loop bodies and branch arms by dominance, and wraps remote nodes. It performs no I/O and executes nothing — it reads a &dyn NodeRegistry and a &dyn CacheStore and returns a plan plus diagnostics.

3 118 lines across 4 files · 1 trait · 10 structs · 4 enums · deps: somatize-core

FileLinesOwns
soma-compiler/src/compiler.rs1 624CompileMode, Diagnostic, DiagnosticLevel, CompileResult, NodeRegistry, SimpleNodeRegistry, Compiler<'a>, private PlanCtx<'b>, free compile / compile_stream
soma-compiler/src/plan.rs936ExecutionPlan and its structural walks (own_node_ids, children, node_count, simplify) plus three renderers
soma-compiler/src/scheduler.rs533WorkerInfo, Assignment, Phase, DistributionPlan, PlanPhase, DataTransfer, schedule
soma-compiler/src/lib.rs2511 re-exports

NodeRegistrysoma-compiler/src/compiler.rs:65

Section titled “NodeRegistry — soma-compiler/src/compiler.rs:65”
pub trait NodeRegistry: Send + Sync {
fn node_meta(&self, node_id: &str) -> Option<NodeMeta>; // required
fn config_hash(&self, node_id: &str) -> Option<CacheKey>; // required
fn meta(&self, node_id: &str) -> Option<FilterMeta> { … } // provided :83
}

The compiler’s only port into the outside world. meta filters out effectful nodes and calls as_filter_meta().

ImplementorCrateDistinguishing behaviour
SimpleNodeRegistrysoma-compiler/src/compiler.rs:146A HashMap populated by hand; used by tests and by callers that have no runtime
NodeCatalogsoma-runtime/src/node_catalog.rs:230The real one — the same registry the executor reads, holding both filters and steps

Object-safe, used as &'a dyn NodeRegistry (soma-compiler/src/compiler.rs:204).

The doc comment at soma-compiler/src/compiler.rs:60 records why the shape is what it is: an earlier version had a required meta and an optional step_meta, which meant .compile() skipped every step’s schema validation while .run() checked them. Making both methods required over the unified NodeMeta is what closed that.

NameRoleKey fieldsOwnsfile:line
Compiler<'a>The one-shot compile objectgraph, registry, mode, diagnostics&'a Graph ──▷, &'a dyn NodeRegistry ──▷soma-compiler/src/compiler.rs:202
CompileResultWhat a compile returnsplan, diagnosticsExecutionPlan ──◆soma-compiler/src/compiler.rs:50
DiagnosticOne warning or notenode_id, level, messagesoma-compiler/src/compiler.rs:28
SimpleNodeRegistryHand-built registryentries: HashMap<String, (NodeMeta, CacheKey)>soma-compiler/src/compiler.rs:91
PlanCtx<'b> (private)Dominance + level analysislevels, dominatorssoma-compiler/src/compiler.rs:162
WorkerInfoA placement candidateid, name, tags, gpu, cpu_cores, active_jobs, max_concurrentsoma-compiler/src/scheduler.rs:15
AssignmentOne node → one workernode_id, worker_id, worker_name, phase, reasonsoma-compiler/src/scheduler.rs:57
DistributionPlanThe scheduler’s outputassignments, phases, data_transfers, warningssoma-compiler/src/scheduler.rs:93
PlanPhaseOne barrier-delimited stagephase_index, phase_type, node_ids, worker_idssoma-compiler/src/scheduler.rs:109
DataTransferAn edge that crosses workersfrom_node, to_node, from_worker, to_worker, transfer_typesoma-compiler/src/scheduler.rs:124

Compiler::compile(self, …) consumes self — it is a one-shot object, not a reusable service.

NameVariants!Whyfile:line
ExecutionPlanSequence(Vec), Parallel(Vec), Execute{node_id}, Step{node_id, handoffs}, Loop{node_id, body, max_iterations, until, carry_from}, Branch{node_id, arms}, Remote{node_id, target, plan}, Composite{node_ids}, Stream{node_ids, chunk_size}, EmptyyesA data enum crossing the wire (SerializedPlan.plan); an old worker must tolerate a new variant rather than fail to deserializesoma-compiler/src/plan.rs:19
CompileModeInference, Differentiable, NoCachenoInternal, three callerssoma-compiler/src/compiler.rs:17
DiagnosticLevelWarning, InfonoInternalsoma-compiler/src/compiler.rs:40
PhaseSequential, Parallel, Trial{trial_index, total}noInternal — Trial is never constructed (!) D-65soma-compiler/src/scheduler.rs:76

ExecutionPlan is recursive in four different shapes, which is why it needs a single traversal rather than eight ad-hoc ones:

ExecutionPlan
├──◆ Vec<ExecutionPlan> Sequence, Parallel
├──◆ Box<ExecutionPlan> Loop.body, Remote.plan
├──◆ Vec<(NodeId, ExecutionPlan)> Step.handoffs
└──◆ Vec<(String, ExecutionPlan)> Branch.arms

ExecutionPlan::children() (soma-compiler/src/plan.rs:142) is that traversal, written out per variant on purpose so a new variant fails to compile — see the comment at :143. Two functions in the same crate do not use it and are wrong as a direct result: (!) D-32.

compile(graph, registry, mode, cache) soma-compiler/src/compiler.rs:1022
└──▷ Compiler<'a>
├──▷ &'a Graph (borrowed, never mutated)
├──▷ &'a dyn NodeRegistry « the only port out »
├──◆ CompileMode
└──◆ Vec<Diagnostic> (accumulated, returned)
CompileResult ──◆ ExecutionPlan
└──◆ Vec<Diagnostic> ──◆ DiagnosticLevel
schedule(plan, workers) soma-compiler/src/scheduler.rs
└──◆ DistributionPlan ──◆ Vec<Assignment> ──◆ Phase
├──◆ Vec<PlanPhase>
└──◆ Vec<DataTransfer>
Functionfile:lineWhat it does
compilesoma-compiler/src/compiler.rs:1022The whole thing. Breakpoint here first.
compile_streamsoma-compiler/src/compiler.rs:1047Produces ExecutionPlan::Stream; refuses DAGs, steps, and chunk size 0
Compiler::plan_for_nodesoma-compiler/src/compiler.rs:531105 lines — the per-NodeKind dispatch, including the dominance-based body/arm claiming
Compiler::validate_control_flowsoma-compiler/src/compiler.rs:28796 lines, 5 levels of nesting — where loop and branch structure is checked
Compiler::validate_schemassoma-compiler/src/compiler.rs:890Edge-by-edge dtype/shape compatibility
schedulesoma-compiler/src/scheduler.rs:164Worker placement (round-robin today)
  • CompositeExecutionPlan is a recursive tree over which execute and children both recurse. → Patterns
  • Visitor-ish exhaustive walkchildren() written per variant so a new variant breaks the build rather than being silently skipped.
  • Strategy via dyn — the registry port. → Patterns
  • Null ObjectExecutionPlan::Empty.
  • Collecting parameterVec<Diagnostic> accumulated through the compile and returned alongside the result rather than logged.
  • D-32resolve_distribution / collapse_differentiable skip Loop and Branch bodies (High)
  • D-65 — the scheduler’s capability model is defined and unused
  • D-17mermaid_nodes and graph_nodes duplicate a whole recursive walk
  • D-18 — two worker-capability models in one workspace
  • Sub-graph compilation drops its diagnostics; plan_for_node guesses for an unknown id — see smaller observations

Execute a compiled plan. It owns the executor, the node catalog, the caches, the effect driver and journal, the study and PBT loops, the samplers and pruners, and the run-directory tracking. It is deliberately not async: tokio is pulled in with default-features = false, features = ["sync"] (soma-runtime/Cargo.toml:14) purely for the broadcast channel, and every concurrency site uses std::thread::scope.

17 449 lines in src/ across 34 files · but ~8 181 of those are inline #[cfg(test)], so ~9 268 lines of production code · 12 traits · 3 enums · deps: somatize-core, somatize-compiler

That test ratio is worth internalizing before reading anything here: executor.rs is 2 472 lines of which 1 184 are tests (they start at :1289); executors/study.rs is 1 915 lines of which 1 470 are tests (:446).

FileLines (code)Owns
soma-runtime/src/executor.rs2 472 (1 288)The plan walker: GraphInfo, RunMode, Context, execute, the three primitives, run_node, per-variant handlers
soma-runtime/src/graph_session.rs839 (485)GraphSession — the top orchestrator; run / fit / forward
soma-runtime/src/node_catalog.rs469 (239)NodeImpl, NodeCatalog — the one registry
soma-runtime/src/strategy.rs1 366 (899)Distributed training execution; TrainingStrategy::fit
soma-runtime/src/forward.rs260 (153)ForwardEnv, ForwardStrategy + Standard / Stream / Batched
soma-runtime/src/event_bus.rs343 (102)EventBus — broadcast + synchronous sinks
soma-runtime/src/pruner.rs264 (169)Pruner, MedianPruner, PercentilePruner
soma-runtime/src/study_io.rs96 (41)StudyIo extension trait — Study::save / load
soma-runtime/src/runner/mod.rs140RunContext<'a>, Runner trait
soma-runtime/src/runner/local.rs181 (86)LocalRunner — the only real runner
soma-runtime/src/runner/remote.rs114Transport trait, RemoteRunner (!)
soma-runtime/src/executors/study.rs1 915 (445)TrialOutcome, TrialContext, TrialExecutor, StudyRunner
soma-runtime/src/executors/stream.rs699 (355)StreamRun, StreamOutput, materialize_buffer
soma-runtime/src/executors/pbt.rs465 (349)PbtConfig, PopulationMember, PbtExecutor, PbtRunner
soma-runtime/src/effects/mod.rs1 464 (549)EffectDriver — the turn loop
soma-runtime/src/effects/journal.rs503 (185)EffectSite, EffectJournal
soma-runtime/src/effects/graph_handler.rs723 (244)GraphHandler, MAX_GRAPH_DEPTH
soma-runtime/src/effects/sleep_handler.rs54 (35)SleepHandler
soma-runtime/src/cache/memory.rs449 (220)MemoryCache + byte-bounded LRU
soma-runtime/src/cache/local.rs353 (196)LocalCache — sharded filesystem cache
soma-runtime/src/cache/tiered.rs228 (104)TieredCache — ordered tiers, promotion on hit
soma-runtime/src/cache/fs_store.rs539 (376)FsActionStore — action records + CAS blobs + pins
soma-runtime/src/cache/gc.rs275 (130)GcPolicy, GcReport, value-density eviction
soma-runtime/src/sampler/mod.rs543 (309)Sampler, GridSampler, RandomSampler, RNG helpers
soma-runtime/src/sampler/bayesian.rs361 (196)BayesianSampler — simplified TPE
soma-runtime/src/tracking/reader.rs935 (891)RunReader + 10 chart-ready DTOs
soma-runtime/src/tracking/summary.rs646 (313)summarize(&RunReader) -> RunSummary
soma-runtime/src/tracking/local_tracker.rs245LocalTracker
soma-runtime/src/tracking/jsonl_sink.rs161JsonlEventSink
soma-runtime/src/tracking/head.rs224 (126).soma/HEAD lineage

Twelve traits are defined here. None uses an associated type or a generic method, so all but StudyIo are object-safe by construction — a deliberate uniformity that makes every seam swappable at runtime.

Runnersoma-runtime/src/runner/mod.rs:124

Section titled “Runner — soma-runtime/src/runner/mod.rs:124”
pub trait Runner: Send + Sync {
fn fit(&self, plan: &ExecutionPlan, ctx: &RunContext<'_>, input: &Value, y: Option<&Value>)
-> Result<(Value, HashMap<String, Value>)>;
fn forward(&self, plan: &ExecutionPlan, ctx: &RunContext<'_>, input: &Value) -> Result<Value>;
}
Implementorfile:lineNote
LocalRunnersoma-runtime/src/runner/local.rs:60The only one used. walk() builds a Context and calls executor::execute
RemoteRunnersoma-runtime/src/runner/remote.rs:91(!) never constructedD-34

(!) Never used as dyn Runner anywhere. Both call sites name LocalRunner concretely.

Transportsoma-runtime/src/runner/remote.rs:18

Section titled “Transport — soma-runtime/src/runner/remote.rs:18”
pub trait Transport: Send + Sync {
fn execute(&self, plan: &ExecutionPlan, filters: &NodeCatalog, input: &Value,
mode: &RunMode, seed: Option<i64>) -> Result<(Value, HashMap<String, Value>)>;
fn get_state(&self, node_ids: &[String]) -> Result<HashMap<String, Value>>;
fn set_state(&self, states: &HashMap<String, Value>) -> Result<()>;
fn get_gradients(&self, node_ids: &[String]) -> Result<HashMap<String, Value>>;
fn apply_gradients(&self, gradients: &HashMap<String, Value>) -> Result<()>;
fn execute_node(&self, node_id: &str, input: Option<&Value>) -> Result<Value>; // provided :61 (!)
}

The wire seam. No implementor lives in this crate — WsTransport is in soma-worker/src/ws_transport.rs:404. Heavily dyn-dispatched: Arc<dyn Transport> in Context (soma-runtime/src/executor.rs:144), GraphSession (:44, :50) and TransportContext (soma-runtime/src/strategy.rs:531).

(!) The provided execute_node builds a throwaway empty catalog and passes seed: NoneD-41.

ForwardStrategysoma-runtime/src/forward.rs:40

Section titled “ForwardStrategy — soma-runtime/src/forward.rs:40”

fn forward(&self, graph: &Graph, env: &ForwardEnv<'_>, x: &Value) -> Result<Value>

Implementorfile:lineDifference
Standardsoma-runtime/src/forward.rs:48compile then run_forward
Streamsoma-runtime/src/forward.rs:63compile_stream(chunk_size) instead
Batched<'a>soma-runtime/src/forward.rs:106Loops store.get_rows and calls run_forward per batch

Samplersoma-runtime/src/sampler/mod.rs:22

Section titled “Sampler — soma-runtime/src/sampler/mod.rs:22”
fn prepare(&mut self, _space: &SearchSpace) {} // provided
fn sample(&mut self, space, trial_index) -> Result<Option<HashMap<String, Value>>>; // required
fn n_trials(&self) -> Option<usize>; // required
fn record_result(&mut self, _params, _value: f64) {} // provided
Implementorfile:lineOverrides
GridSamplersoma-runtime/src/sampler/mod.rs:151prepare — lazy mixed-radix index, never materializes the product
RandomSamplersoma-runtime/src/sampler/mod.rs:216
BayesianSamplersoma-runtime/src/sampler/bayesian.rs:162record_result — simplified TPE, γ = 0.25

fn should_prune(&self, metric_name, current_value, step, history) -> Option<String> — returning the reason rather than a bool, so the event carries it. Implementors: MedianPruner (:58), PercentilePruner (:127), which are the same 20 lines with a different statistic (!) D-14.

TrialExecutor::execute_trial(&self, params, ctx) -> Result<TrialOutcome> (soma-runtime/src/executors/study.rs:133), implemented by FnTrialExecutor<F> (:144). PbtExecutor has train + evaluate (soma-runtime/src/executors/pbt.rs:55), implemented by FnPbtExecutor<T, E> (:63). Both are the callback-adapter pattern: the only implementor wraps a closure, so the trait exists to be dyn-able, not to be subclassed.

StrategyContext / StrategyExecutor / GradientAggregator / StateAggregator

Section titled “StrategyContext / StrategyExecutor / GradientAggregator / StateAggregator”

The distributed-training seam, and the crate’s clearest use of extension traits — the traits are defined here, but implemented on foreign types from soma-core, keeping “core holds contracts, runtime holds execution” (soma-runtime/src/strategy.rs:1).

Traitfile:lineImplemented on
StrategyContextsoma-runtime/src/strategy.rs:33TransportContext<'_> (:589) — 6 required, 3 provided, two of which default to refusing
StrategyExecutorsoma-runtime/src/strategy.rs:120TrainingStrategy (foreign, :145)
GradientAggregatorsoma-runtime/src/strategy.rs:132GradientAggregation (foreign, :461) (!) D-21
StateAggregatorsoma-runtime/src/strategy.rs:139FederatedAggregation (foreign, :486)

(!) StrategyContext::execute_on_worker carries a dead plan: &serde_json::Value parameter — D-43.

StudyIosoma-runtime/src/study_io.rs:19

Section titled “StudyIo — soma-runtime/src/study_io.rs:19”

The one non-object-safe trait (Sized + impl Trait args + a static load). Implemented on Study (foreign, :28) so that a soma-core type gets filesystem persistence without soma-core gaining a filesystem.

Trait (from)Implementorsfile:line
CacheStore (core)MemoryCache, LocalCache, TieredCache, FsActionStorecache/memory.rs:129, cache/local.rs:115, cache/tiered.rs:30, cache/fs_store.rs:304
BlobStore + ActionCache (core)FsActionStore (both)cache/fs_store.rs:255, :281
NodeRegistry (compiler)NodeCatalognode_catalog.rs:230
EventSink (core)JsonlEventSinktracking/jsonl_sink.rs:124
Tracker (core)LocalTrackertracking/local_tracker.rs:90
EffectHandler (core)GraphHandler, SleepHandlereffects/graph_handler.rs:136, effects/sleep_handler.rs:20
NameRoleKey fieldsOwnsfile:line
GraphSessionThe top orchestratorgraph, catalog, cache, bus, 2× transport, identities, driver, fittedGraph ──◆, NodeCatalog ──◆, Arc<dyn CacheStore> ──◇soma-runtime/src/graph_session.rs:38
NodeCatalogTHE registry — filters and stepsnodes: HashMap<String, NodeImpl>, states: Arc<dyn StateStore>Arc<dyn Filter|Step> ──◇; clones share the state store (:75)soma-runtime/src/node_catalog.rs:79
ContextThe executor’s mutable run state12 fields (!)value store, execution order, hash memo (all private)soma-runtime/src/executor.rs:124
RunContext<'a>A runner’s borrowed viewcatalog, cache, events, run id, GraphInfo, seed, driverall borrowed except GraphInfosoma-runtime/src/runner/mod.rs:32
ForwardEnv<'a>A forward strategy’s borrowed viewcatalog, cache, bus, store, driverall borrowedsoma-runtime/src/forward.rs:25
GraphInfoTopology, not orderpredecessors: HashMap<String, Vec<String>>soma-runtime/src/executor.rs:28
EffectDriverThe turn loophandlers, journal, bus, catalogVec<Arc<dyn EffectHandler>> ──◇soma-runtime/src/effects/mod.rs:57
EffectJournalRecord once, replay foreveractions, blobs, enabledArc<dyn ActionCache> ──◇, Arc<dyn BlobStore> ──◇soma-runtime/src/effects/journal.rs:51
EffectSite<'a>The impure-effect keyrun_id, node_id, turn, index— (Copy)soma-runtime/src/effects/journal.rs:36
GraphHandlerA graph as a tool for an agentlibrary, cache, step_runtimeNodeCatalog ──◆; recursion capped at 8soma-runtime/src/effects/graph_handler.rs:47
EventBusDual-path pub/subbroadcast::Sender<Event>, RwLock<Vec<Arc<dyn EventSink>>>sinks ──◇soma-runtime/src/event_bus.rs:22
StreamRunThe chunk drivernodes: Vec<StreamNode>, chunk_countper-node base state, barrier buffer, evolving statesoma-runtime/src/executors/stream.rs:73
StreamOutputChunk accumulatorall_data, result_shape, non_tensorsoma-runtime/src/executors/stream.rs:283
StudyRunnerThe trial loopevent_bus, trackersoma-runtime/src/executors/study.rs:160
TrialContextWhat user trial code seesobjective, pruner, history, bus, Arc<Mutex<TrialShared>>soma-runtime/src/executors/study.rs:48
PbtRunner / PbtConfig / PopulationMemberPopulation-based trainingsoma-runtime/src/executors/pbt.rs:84, :22, :41
TransportContext<'a>The StrategyContext impltransports, plan, catalog, seed, Mutex<Vec<states>>, identitiesVec<Arc<dyn Transport>> ──◇soma-runtime/src/strategy.rs:530
MemoryCache / LocalCache / TieredCache / FsActionStoreThe cache tierssee D3cache/memory.rs:16, local.rs:15, tiered.rs:11, fs_store.rs:40
RunReaderRun dir → chart-ready DTOsdir: PathBufsoma-runtime/src/tracking/reader.rs:36
LocalTracker / JsonlEventSinkRun dir writerstracking/local_tracker.rs:27, jsonl_sink.rs:19

Only three, and none is #[non_exhaustive] — all three are internal control-flow enums every consumer must decide over.

NameVariantsWhy exhaustivefile:line
RunModeForward, Fit { y: Option<Value> }Two whole execution loops collapsed into one parameter (:82); (!) it also crosses the wire — D-45soma-runtime/src/executor.rs:92
NodeImplFilter(Arc<dyn Filter>), Step(Arc<dyn Step>)”the only place in the workspace that names the two kinds” (:31)soma-runtime/src/node_catalog.rs:37
TrialOutcomeCompleted(Vec<MetricRecord>), Pruned { step, reason }Separates control flow from error — pruning is not a failuresoma-runtime/src/executors/study.rs:24

The single most important structure in the workspace. One registry, one execution site, and exactly one match that tells a filter from a step.

«trait» Filter «trait» Step
(soma-core/src/filter.rs:120) (soma-core/src/step.rs:250)
fit / forward poll -> Transition
▲ ▲
│ │
┌─────┴──────┐ ┌─────┴─────────────┐
PyFilterBridge SubprocessFilter ReactStep PyStepBridge ResearchStep
LlmStep JudgeStep
│ │
└────────────┐ ┌──────────────┘
▼ ▼
[enum] NodeImpl { Filter | Step }
soma-runtime/src/node_catalog.rs:37
NodeCatalog ──◇ Arc<dyn StateStore> « shared across clones »
soma-runtime/src/node_catalog.rs:79
┌─────────────┴──────────────┐
▼ ▼
«trait» NodeRegistry executor::run_node
(the compiler's port) soma-runtime/src/executor.rs:816
soma-compiler/…:65 │
│ ▼
▼ run_node_inner « THE match »
NodeMeta ◁── From<FilterMeta> soma-runtime/src/executor.rs:1058
◁── From<StepMeta> Filter → forward()
soma-core/src/node.rs:72 Step → driver.run()

From<StepMeta> for NodeMeta (soma-core/src/node.rs:132) sets cacheable: false, deterministic: false, which is why “a step is not output-cacheable” needs no if is_step anywhere — the executor’s existing cacheability guard (soma-runtime/src/executor.rs:677) reads it as data.


Graph ──▷ compile() ──▷ ExecutionPlan ──▷ LocalRunner::walk ──▷ Context
executor::execute(plan, ctx, catalog, cache)
soma-runtime/src/executor.rs:367
┌────────────┬──────────┬─────────┬──────────┬────────────┼──────────┐
▼ ▼ ▼ ▼ ▼ ▼ ▼
Sequence Parallel Loop Branch Remote Composite Stream
(recurse) thread::scope :460 :531 :601 composite_fit :1208
:1084 :953 │
└────────────┴──────────┴─────────┴──────────┴────────────┘ │
│ │
▼ ▼
run_node :816 StreamRun::run_compute
│ stream.rs:194
┌───────────────┼───────────────┐ │
▼ ▼ ▼ │
output_key :670 compute_node :692 store_output :717 ◁─────────┘
guard+derive+ catch_unwind provenance « the three shared
seed salt │ primitives »
run_node_inner :1058
Filter → forward | Step → EffectDriver::run

The three primitives are the whole point: run_node composes them once for the batch path, StreamRun composes them per chunk. (!) But everything around them is written twice, and the two copies have drifted — D-11.


Two content-addressed systems that look similar and are not. A filter memoizes by content; a step journals by site.

«trait» CacheStore soma-core/src/cache.rs:204
get/put/exists/remove/metadata
+ put_computed, get_located, tier « defaulted »
┌─────────────┬───────┴───────┬────────────────┐
MemoryCache LocalCache TieredCache FsActionStore
LRU, max_bytes aa/bb/hex.json ordered tiers, two tables + pins
memory.rs:16 local.rs:15 promotes on hit fs_store.rs:40
(!) no bound tiered.rs:11 │
(!) promotion loses ├──▷ «trait» ActionCache
Origin │ action records, kept forever
└──▷ «trait» BlobStore
BLAKE3 CAS, evictable by gc.rs
CacheKey derivation soma-core/src/cache.rs:18
state = hash(config ‖ x ‖ y) for_state
output = hash(config ‖ state ‖ input_hash) for_output
+ salt_with_seed(seed) executor.rs:634
→ downstream keys use input *content* hashes, so an unchanged
intermediate cuts off the rest of the graph early
EffectJournal soma-runtime/src/effects/journal.rs:51
pure effect → key = content only « reusable across runs »
impure effect → key = b"sited" ‖ run ‖ node ‖ turn ‖ index
lookup() replays; record() writes; Failed results are never recorded (:157)

Annotated call chains. These are the fastest way back into the code: pick one, set a breakpoint at the top, and step.

They read as five separate chains and they are not — they are five entry points into one graph, and the six hops they share are where the architecture’s load-bearing claims live. Call Paths draws that overlap, which two blocks three hundred lines apart cannot.

GraphSession::forward(x) graph_session.rs:333
└─ forward_with(x, &Standard) graph_session.rs:334
├─ run_driver() → driver.clone().with_catalog(…) graph_session.rs:145
└─ Standard::forward(graph, &ForwardEnv{…}, x) forward.rs:49
├─ compile(graph, catalog, Inference, Some(cache)) forward.rs:51
└─ run_forward(graph, &plan, env, x) forward.rs:77
├─ timestamp_id("forward") forward.rs:83
├─ RunContext::new(…, GraphInfo::from_graph(graph)) forward.rs:84
└─ LocalRunner.forward(plan, &ctx, x) forward.rs:94
└─ walk(plan, ctx, input, RunMode::Forward) runner/local.rs:26
├─ Context::new(…).with_graph_info(…).with_seed(…) runner/local.rs:33
├─ exec.set(input_key(first), input) runner/local.rs:40
├─ executor::execute(…) → (b) runner/local.rs:44
└─ last_output(&exec) runner/local.rs:51
execution_order().rev().find(!reserved)

Stream diverges only at soma-runtime/src/forward.rs:65 (compile_stream); Batched at soma-runtime/src/forward.rs:107 (its own get_rows loop calling run_forward per batch).

(b) executerun_node → the three primitives

Section titled “(b) execute → run_node → the three primitives”
execute(plan, ctx, catalog, cache) executor.rs:367
├─ Empty → Ok(()) :374
├─ Execute{id} → execute_node(id, &[], …) :377
├─ Step{id, handoffs} → execute_node(id, handoffs, …) :379
├─ Sequence(v) → for each: execute(…) :383
├─ Parallel(b) → execute_parallel :1084
├─ Loop{…} → execute_loop :460
├─ Branch{…} → execute_branch :531
├─ Remote{…, target: _} → execute_remote (! target discarded) :601
├─ Composite{ids} → composite_fit (fit) | per-node (fwd) :953
├─ Stream{ids, size} → execute_stream → (d) :1208
└─ other → Err("newer compiler") :445
run_node(node_id, ctx, catalog, cache) executor.rs:816
├─ node = catalog.node(id)?.clone(); meta = node.meta() :824
├─ input = resolve_input(node_id, ctx) :1173
│ 0 preds → execution_order.last() (!) D-44
│ 1 pred → that pred
│ n preds → merged JSON object, keyed by predecessor
├─ fitted = fit_state_if_needed(…) :1002
│ guard: mode.is_fit() && meta.trainable()
│ key = salt_with_seed(CacheKey::for_state(config, x, y), seed)
│ hit → reuse | miss → catch_unwind(filter.fit) → put_computed
├─ ▸ PRIMITIVE 1 output_key(node, meta, state, input_hash, seed) :670
│ guard: !(meta.cacheable && meta.deterministic) → None
├─ cache.get_located(key) hit → emit NodeCacheHit, return Produced :862
├─ miss → emit NodeCacheMiss, emit NodeStarted :876
├─ ▸ PRIMITIVE 2 compute_node(…) = catch_unwind(run_node_inner) :692
│ └─ run_node_inner :1058
│ ├─ NodeImpl::Filter(f) → f.forward(input, state) → Produced :1066
│ └─ NodeImpl::Step(s) → driver.run(s, run_id, node_id, input) → (c) :1069
└─ match outcome :905
├─ Produced(out) → ▸ PRIMITIVE 3 store_output(…) :717
│ → maybe_spill → set_virtual → NodeCompleted
├─ HandOff{target, carry} → ctx.set(node, carry); NodeCompleted :931
└─ Paused{turn, reason} → nothing stored :942
back in execute_node executor.rs:748
├─ Produced → Ok(())
├─ HandOff → select_handoff(:770) → execute(that plan)
└─ Paused → Err(SomaError::Suspended{…})

execute_parallel (soma-runtime/src/executor.rs:1084) is worth reading in full: it marks execution_order.len(), opens a std::thread::scope, gives each branch a ctx.snapshot() (!) D-61, then merges back only the write set — the entries each branch appended past the mark.

EffectDriver::run(step, run_id, node_id, input) effects/mod.rs:107
├─ journal = self.journal.with_enabled(enabled && meta.journal) :116
└─ for turn in 0..meta.max_turns :127
├─ emit AgentTurnStarted
├─ ctx = StepCtx::new(…).with_history(&history) :134
├─ transition = step.poll(&ctx)? :138
└─ match transition :146
├─ Await(effects) → perform_all(…) :440
│ ├─ emit EffectRequested per effect
│ ├─ thread::scope: one thread per effect :459
│ │ └─ perform_one(journal, EffectSite{run,node,turn,i}) :519
│ │ ├─ journal.lookup(site, effect)? → replayed :527
│ │ ├─ handlers.iter().find(|h| h.handles(effect)) :531
│ │ ├─ handler.perform(effect)? :543
│ │ └─ journal.record(…) (Failed is never recorded) :545
│ └─ usage += …; emit ToolCalled / EffectCompleted → history.push(results) :159
├─ Done(v) → Ok(NodeOutcome::Produced(v)) :167
├─ Goto{target, carry} → Ok(NodeOutcome::HandOff{…}) :172
├─ Suspend{reason} :187
│ ├─ journal.lookup(site, suspension_effect(reason))?
│ │ Some → emit Resumed, continue « the resume path »
│ └─ None → emit Suspended → Ok(NodeOutcome::Paused{…}) :206
└─ Spawn{specs, join} → spawn_all(…) :316
child ids "{node_id}/{label|turn.index}"; thread::scope;
RECURSES into self.run per child :365
JoinPolicy: All | AllSettled | First (! First still joins all)
loop exhausted → Err("did not finish within N turns") :240

resume_with(run_id, node_id, turn, reason, answer) (soma-runtime/src/effects/mod.rs:279) writes the answer at the same site, so the next run replays into it. That is the whole resume mechanism — there is no separate checkpoint format.

Two entry points into one object. Locally, execute_stream (soma-runtime/src/executor.rs:1208) chunks the input and drives it. Remotely, soma-worker holds the StreamRun and its Context alive in active_streams between WebSocket messages and calls the same three methods itself.

execute_stream executor.rs:1208
├─ refuse if mode is Fit :1220
├─ chunks = chunk_value(input, chunk_size) :1264
├─ run = StreamRun::new(node_ids, catalog) (steps → Err) stream.rs:83
├─ per chunk: run.process_chunk(chunk, ctx, cache) stream.rs:130
│ └─ per node i:
│ ├─ Barrier → buffer the value, stop the cascade :140
│ └─ else → current = run_compute(i, current, …) :194
│ ├─ first touch → emit NodeStarted :203
│ ├─ state = evolving.or(base_state) :213
│ ├─ ▸ output_key(…) :217
│ ├─ cache hit → counters only (!) no event :220
│ ├─ ▸ compute_node(…) :233
│ └─ ▸ store_output(…); evolving update :239
├─ run.flush(ctx, cache) → materialize_buffer per barrier node stream.rs:152
├─ run.finish(ctx) → one NodeCompleted per node, stream.rs:167
│ "stream: N chunks, H hits, M misses"
└─ ctx.set(last_id, output.finish()) stream.rs:310

FixedState keys are identical to the batch path’s, so a single-chunk stream and a plain forward share one cache line. That invariant is what makes the three primitives worth having.

StudyRunner::run(study, sampler, executor) executors/study.rs:187
├─ sampler.prepare(&study.search_space) :193
├─ RESUME: replay completed trials into sampler.record_result :205
├─ pruner = build_pruner(&study.pruning) :407
├─ trial_index = study.trials.len() « the resume point » :218
└─ loop :228
├─ config_index = i / n_seeds ; seed_slot = i % n_seeds :229
│ seed_slot > 0 → reuse the previous trial's params minus "seed"
│ else → sampler.sample(space, config_index)?
├─ params += {"seed": …} += study.frozen :250
├─ ctx = TrialContext{objective, pruner, history, bus, shared} :270
├─ outcome = executor.execute_trial(&params, &ctx) :281
│ user code calls ctx.report(name, value, step) :70
│ → push metric, emit TrialMetric, ask the pruner
├─ match (outcome, pruned) :287
│ (Ok(_), Some(..)) | (Ok(Pruned{..}), None) → Pruned
│ (Ok(Completed(m)), None) → Completed
│ (Err(e), _) → Failed
├─ sampler.record_result(…); best-trial check; StudyProgress :335
└─ save_study(study) (! rewrites the whole file per trial) :361
PbtRunner::run(config, executor) executors/pbt.rs:97
├─ rng_state = 42 (! hardcoded, no seed field) :103
├─ initialize_population :195
└─ for generation in 0..generations :108
├─ TRAIN each member (! failure → warn, keeps stale state) :116
├─ EVAL each member (failure → NEG_INFINITY, counted) :135
├─ sort by fitness desc :156
└─ evolve: exploit (truncation | binary tournament) :215
then explore (perturbation | resample)
GraphSession graph_session.rs:38
├──◆ Graph
├──◆ NodeCatalog ──◇ Arc<dyn StateStore> « shared across clones »
│ └──◆ HashMap<String, NodeImpl> ──◇ Arc<dyn Filter | Step>
├──◇ Arc<dyn CacheStore>
├──◇ Arc<EventBus>
│ ├──◆ broadcast::Sender<Event> « lossy subscribers »
│ └──◆ RwLock<Vec<Arc<dyn EventSink>>> « lossless sinks »
├──? Option<Arc<dyn DataStore>>
├──? Option<Arc<dyn Transport>> ┐ (!) two independent transport fields
├──◆ Vec<Arc<dyn Transport>> ┘ D-04
└──? Option<EffectDriver>
├──◆ Vec<Arc<dyn EffectHandler>> → GraphHandler, SleepHandler, llm, tools
├──◆ EffectJournal ──◇ Arc<dyn ActionCache> + Arc<dyn BlobStore>
└──? Option<Arc<NodeCatalog>> « needed only for Transition::Spawn »
RunContext<'a> ──▷ builds ──▷ Context runner/local.rs:33
(borrowed view) (owned, &mut through the walk)
GraphHandler ──◆ NodeCatalog
└──? StepRuntime ──▷ child_driver() ──▷ EffectDriver ──▷ GraphHandler
« a real cycle, capped at MAX_GRAPH_DEPTH = 8 »
effects/graph_handler.rs:28

There are zero impl From blocks in this crate. Every conversion is foreign (FilterMeta → NodeMeta in soma-core) or implicit (? on io::Error).

  • StrategyForwardStrategy, Sampler, Pruner, StrategyExecutor. → Patterns
  • Template methodLocalRunner::walk shared by fit and forward, differing only by RunMode; documented at soma-runtime/src/runner/local.rs:22 as the fix for two divergent loops.
  • Extension traitStudyIo for Study, StrategyExecutor for TrainingStrategy. → Patterns
  • Chain of responsibilityEffectDriver::perform_one, first handler whose handles() claims the effect.
  • Event sourcing / durable executionEffectJournal; suspension modelled as an effect so resume needs no separate format.
  • ObserverEventBus, dual lossy/lossless path.
  • MementoContext::snapshot for parallel branches, merged by write-set diff.
  • DecoratorTieredCache is a CacheStore of CacheStores that promotes on read.
  • Callback adapterFnTrialExecutor<F>, FnPbtExecutor<T, E>.
  • Scoped-thread fan-out — three sites, no async runtime, rationale at soma-runtime/src/effects/mod.rs:12.
  • Two-phase commit — temp + fsync + rename in four places (two of them weaker (!)).
  • Forward-compatible refusal — unknown variants return an error naming the situation rather than guessing. Applied consistently; a genuine strength.

High

  • D-11 — stream path re-implements run_node; emits no cache events
  • D-21mean_by_key panics on an empty slice, reachable from Python
  • D-41 — remotes run with an empty catalog and unsalted keys

Medium

  • D-03 Context god object · D-04 two transport fields · D-07 RunReader
  • D-12 four write_atomics · D-13 the run bracket ×4
  • D-22 suspension key collision
  • D-34 RemoteRunner dead · D-36 unreached methods
  • D-42 Remote target discarded · D-43 dead JSON param · D-44 resolve_input fallback · D-46 promotion loses provenance
  • D-61 snapshot cost · D-62 O(n) LRU · D-63 reader re-parses · D-64 O(trials²)
  • D-71 four mutex-poison policies

LowD-14, D-45, D-48, D-49, D-66D-70, D-72D-73, D-81, D-93

10 integration files, 5 443 lines. Several are named after the bug they prevent, which is the most useful thing a test file name can do.

FileLinesCovers
soma-runtime/tests/agentic_step.rs1 271The step-as-node seam: compile, run_node, handoffs, suspend/resume, journal replay, spawn fan-out
soma-runtime/tests/tracking.rs1 175JsonlEventSink (seq, append, torn-tail repair), LocalTracker, RunReader, summarize, HEAD lineage
soma-runtime/tests/coverage_boost.rs867Explicitly path-coverage-driven: spill, get_virtual, state persistence, remote fallback
soma-runtime/tests/integration.rs529End-to-end fit → forward → cache hit → invalidation
soma-runtime/tests/memory_usage.rs462A tracking allocator asserting Batched and Stream do not grow the heap with batch count
soma-runtime/tests/fit_through_run_node.rs450Regression suite for the fit/forward unification
soma-runtime/tests/pbt_integration.rs251PbtRunner against real trainable filters
soma-runtime/tests/session_steps.rs183GraphSession::with_driver reaching steps from run / fit / forward
soma-runtime/tests/topology.rs168Forward follows graph topology, not plan order — the diamond regression
soma-runtime/tests/fit_determinism.rs87fit is reproducible

Not covered by anything: RemoteRunner (dead), TieredCache promotion provenance, ModelParallel against a real transport (unit tests use a mock StrategyContext at soma-runtime/src/strategy.rs:996), and MemoryCache eviction under concurrent parallel branches.