Intents and the resolver¶
IntentResolver
| Kind | Engine core — the only writer of the graph |
| Project | NextIteration.Core/Intents/ |
| Exposes | the Intent records; ResolveAndApply(graph, batch) |
| Consumes | one batch of intents per phase |
| Produces | graph writes; intent_applied / intent_rejected / intent_conflict events; the change set |
| Status | designed · Phase 1 (#28) |
| PLAN | §4, §11.1, §18.1, §18.5, §18.6, §18.8 |
| Player | Water and buckets (the bucket race) |
Systems never mutate the graph. They yield intents — proposed changes — and the resolver validates, resolves conflicts, applies, clamps and records them. This indirection is what gives the engine conflict resolution, determinism, and a complete audit of what was attempted.
Intent types¶
abstract record Intent(int Priority, int SystemOrder, string SourceSystem)
{
IReadOnlyDictionary<string, float>? Inputs { get; init; } // optional: what this was computed from — for the trace (§13.5)
}
record SetAttributeIntent // health = 50
record DeltaAttributeIntent // temperature += 5
record AddEdgeIntent / RemoveEdgeIntent
record SetEdgePropertyIntent // door.state = "locked"; a fact's value (Phase 1)
record CreateNodeIntent / DestroyNodeIntent
record PropagationIntent(Source, Channel, Attribute, InitialIntensity, DeltaFactor, MinThreshold) // engine expands to deltas (§11.1)
SourceSystem is system:<name>, rule:<id> or player:<id> — the same vocabulary the trace and the viewer use.
Resolution policy¶
All of a phase's intents are collected and resolved in one batch, grouped by target (node, key):
| Target | Strategy |
|---|---|
| Numeric attribute, deltas | summed — deltas compose; two systems warming a room add up |
| State attribute or edge property, sets | priority wins; equal priority → StableHash(tick, actorId, intentOrdinal) tiebreak (an input that differs between rivals), and an intent_conflict event |
contains edges |
single-container rule: a node has exactly one container; several AddEdge(contains, X) in one batch → one winner, the rest rejected with intent_conflict. This is what makes a contested bucket resolve without any system knowing there was a contest, and why water lives in the bucket. |
| Create / destroy | validated for dependencies, first valid wins; a destroyed node's contents must be handled (dropped) |
PropagationIntent |
BFS over edges with permeability.<channel>, emitting deltas attributed to the requesting system; reach bounded by permeability × MinThreshold, never a hop cap |
Within the batch, application is sequential in resolution order (fifty attacks land one after another).
Bounds¶
After applying, values are clamped to the world schema's bounds (§18.5, §18.8): soft bounds for system deltas, hard bounds for everything. Every depletable attribute must declare a min — a schema entry for food_value, water_level, fuel, wood_count without one is rejected at load, because eight NPCs reading the same frozen food_value = 2 and each taking a bite would otherwise sum to −2.
What the resolver leaves behind¶
- The applied changes, each with a new
Generation. - The per-tick change set
(node, attribute, generation)— read by rules (Phase 3) and the trace. - Events:
intent_applied,intent_rejected { reason },intent_conflict { rival }, plus whatever the intent's semantics emit (item_taken,ignition…). - The trace's intent records, in resolution order, with ordinals (see Causal trace).
History¶
| Phase | Change |
|---|---|
| 1 | Introduced with per-phase batching, the single-container rule, a tiebreak that differs between rivals, required lower bounds, SetEdgePropertyIntent, optional Inputs. |