Trace records¶
designed · Phase 1 (#31) · PLAN §13.5, §10.1, §4.2
Every record carries tick, phase and an ordinal within the tick (resolution order). Sources are system:<name>, rule:<id>, player:<id>.
| Record | Fields | Indexed by |
|---|---|---|
IntentApplied |
ordinal, source, intent { type, target, key, value \| delta \| edge \| component }, inputs?, generation (the write's new generation), events[] (ids produced) |
ordinal; (node, attribute, generation); entity; source; time |
IntentRejected |
as above + reason |
same |
IntentConflict |
as above + rival (ordinal of the winner) |
same |
Decision |
actor, winner: Option, runnersUp: Option[≤3], weights { family → factor } |
actor; time; need |
Event |
id = (tick << 20) \| ordinal, type, source, location, data, producedBy (intent ordinal) |
id; entity (source and any node in data); type; time |
Propagation |
ordinal, source, channel, hops[] { node, intensity } |
source node; time |
NodeDestroyed |
ordinal, node, kind, template, name, components[] |
node |
PlayerAction |
player, action (the queued JSON), ordinals[] (the intents it became) |
player; time |
RuleFired (Phase 3) |
rule, trigger { node, attribute, generation } |
rule |
OracleCall (Phase 5) |
requestType, oracleLogId, prompt (gap or dilemma) |
actor |
TickDigest |
digest — stable hash over the tick's applied intents in order |
tick |
Option as recorded: { actor, action, target, need, score, reason, inputs { "kira.hunger": 72, "kira.perceived_danger": 0.8 }, publisher }.
Storage format (warm)¶
One JSON object per line, one segment file per tick range: saves/{universeId}/trace/{from}-{to}.jsonl with a sidecar {from}-{to}.index.json listing the ids, entities, change keys and time range in the segment. Segments are append-only; the hot buffer rebuilds from the newest on load.
Prose rendering (viewer)¶
Each record kind has a prose template on the client, which is where engine vocabulary turns into words: a Decision → "{name} {verb} because {reason}"; an IntentApplied with inputs from FireSystem → "{target} caught fire because {source}'s heat crossed {ignition_point}"; a PlayerAction → "…was {verb} by you, on the {world time}". The trace itself carries no prose beyond Reason.