Skip to content

From perception to Explain this

designed · Phase 1 (#31, #35–#38, #41) · PLAN §7.2–§7.4.2, §7.7, §13.5

The path one decision takes, with the component that owns each step and what it leaves behind for Explain this.

 tick N, Agency phase
 ┌──────────────────┐   perceived_entities, perceived_danger, perceived_warmth (attributes)
 │ PerceptionSystem │──▶ knows edges: {food_value: {value, observed_at, provenance}}            ─┐
 └──────────────────┘                                                                            │ graph
 ┌──────────────────┐   hunger += PerTick(hunger_rate_per_day)                                   │
 │ HungerSystem     │──▶                                                                        ─┘
 └──────────────────┘
 tick N+1, Agency phase (reads what the above wrote)
 ┌──────────────────┐   Option(actor, "move", target, Need:"safety", Score, Reason, Inputs{…})
 │ BaselineOptions  │──▶ Option(actor, "eat", bush,    Need:"hunger", …)                      ──▶ ctx.Options (bus, transient)
 └──────────────────┘   Option(actor, "fetch_water", bucket, Need:"safety", …)
 ┌──────────────────┐   per actor: × personality weight → argmax, StableHash tiebreak
 │ DecisionSystem   │──▶ SetAttribute(current_action, _target, _reason)                        ──▶ graph
 └──────────────────┘   decision record {winner, runners-up[3], inputs, weights}               ──▶ trace
 tick N+2, Agency phase
 ┌──────────────────┐   move:  RemoveEdge(contains old) + AddEdge(contains new)
 │ ActionSystem     │──▶ eat:   Delta(hunger, −meal_value) + Delta(bush.food_value, −bite_size) ──▶ batch
 └──────────────────┘   intents carry Inputs where a reader would ask why
 ┌──────────────────┐   sums deltas, priority for sets, one container per node, bounds
 │ IntentResolver   │──▶ intent_applied / intent_conflict events                               ──▶ graph + trace
 └──────────────────┘
 ┌──────────────────┐   Explain(eventId): event → intent (ordinal) → source (system:Decision)
 │ Causal trace     │     → decision record → its Inputs (perceived_danger=0.8, caution=0.8)
 └──────────────────┘     → WhyChanged(kira, perceived_danger, gen) → PerceptionSystem intent
                            → its Inputs (inn.temperature=310) → WhyChanged(inn, temperature, gen)
                            → FireSystem propagation → ignition intent → … → player:harness action
 ┌──────────────────┐
 │ Viewer           │──▶ "Kira left the square because the inn was burning and she is cautious.
 └──────────────────┘     The inn caught fire because the smithy's heat crossed 250°. The smithy was set alight by you."

The contracts along the way

Step Owner Writes Reads PLAN
Sense PerceptionSystem perceived_*, knows edges containment, permeability.light/sound, salient attributes §7.2, §7.3
Need HungerSystem hunger hunger_rate_per_day §7.4.1
Offer BaselineOptions options (bus) perceived_*, hunger, energy, view.Facts/Known — never a target's live attributes §7.4.1, §7.4.2
Choose DecisionSystem current_action*; decision record ctx.Options, personality_* §7.4.2, §7.6
Do ActionSystem intents per §7.7 current_action* §7.7
Apply Resolver graph; events batch §18.1, §18.6
Remember Causal trace records applied intents, decisions, events §13.5
Tell Viewer (Explain this) prose Explain, History §13.4

What makes the chain possible

Three small things, each cheap, each required: every event has an id and the ordinal of the intent that produced it; every decision record keeps the runners-up and the inputs; and intents may carry Inputs — the snapshot values they were computed from — so that a physics link ("the inn caught fire because…") has a next hop. A system that omits Inputs ends the chain at itself, which is acceptable but shallower.

See also

The tick · Option bus · Causal trace · Player: Explain this