kaldamus entity reference · EventGraph

Event graph

EventGraph owns no data of its own — it's a full, disposable recomputation of what events.json, event_dependencies.json and event_factors.json already say, plus one structural, falsifiable check: does the market price an effect above its necessary cause?

01

Three shapes, one row

Sub-shapeOne row perHolds
GraphNodeeach Event (always) or referenced Factor (optional layer)id · kind · label / category · event-only: status, probability, resolved_outcome · computed: in/out_degree
GraphEdgeeach depends_on (event→event) or has_factor (event→factor)source / target · type · kind (hard/soft) · computed: coherence_violation, precondition_met
GraphMetricsthe graph (singleton rollup)node / edge counts · components · longest_hard_chain · hard_cycle · coherence_violation_count · top_degree
Concept
Example
Not two joined entities — one singleton row (id="current") holding three co-equal sub-shapes: the nodes, the edges between them, and a rollup of both.
graph.json = one row
GraphNode is heterogeneous: event nodes always exist, factor nodes are an optional layer.
--no-factors event-only graph
A factor node is materialized only when some event references it — an unreferenced Factor row never appears here at all.
orphan Factor absent from the graph
02

How update-event-graph runs

flowchart TD
    classDef srcNode fill:#16233b,stroke:#5a8fd4,stroke-width:1.5px,color:#dfe7f5;
    classDef factorNode fill:#241d0c,stroke:#c9a84c,stroke-width:1.5px,color:#f3e6bd;
    classDef decisionNode fill:#12151f,stroke:#5a6178,stroke-width:1px,color:#c9cbd6;
    classDef computeNode fill:#161822,stroke:#e0e0e0,stroke-width:2px,color:#f0f0f0;
    classDef deadEnd fill:#12151a,stroke:#3a4058,stroke-width:1px,color:#7a8296,stroke-dasharray:3 3;

    A["events.json"]:::srcNode --> D["node per Event"]:::srcNode
    B["event_dependencies.json"]:::srcNode --> E["depends_on edges
drop if either endpoint missing"]:::srcNode D --> E E --> F{"kind?"}:::decisionNode F -- "soft" --> H["coherence_violation = false"]:::computeNode F -- "hard" --> G{"P(source) > P(target)?"}:::decisionNode G -- "yes" --> I["coherence_violation = true"]:::computeNode G -- "no" --> H C["event_factors.json
+ factors.json"]:::factorNode --> K{"--no-factors?"}:::decisionNode K -- "no (default)" --> L["node per referenced Factor
+ has_factor edges"]:::factorNode K -- "yes" --> M["factor layer omitted"]:::deadEnd I --> N["graph-wide metrics:
components, longest_hard_chain,
hard_cycle, coherence_violation_count"]:::computeNode H --> N L --> N M --> N N --> O["graph.json (id='current')
fully overwritten"]:::factorNode

Every run of update_event_graph.py. Blue = event-side inputs, gold = factor-side inputs and the final write.

Concept
Example
Pure assembly — no LLM, no network: union the four tables, run one structural check per hard edge, roll up graph-wide metrics, overwrite the row.
reads 4 tables writes 1 row
Closed-world holds here too: a depends_on edge with an off-corpus endpoint is dropped, so every rendered edge connects two real event nodes.
off-corpus endpoint edge dropped
Coherence is defined only for hard edges — a soft edge isn't a logical necessity, so there's nothing to falsify.
soft edge no coherence check
Runs after dependency discovery and before reports; pure-local, no MLX, no network — cheap to re-run on any platform.
placed late in update-all
03

Derived, not source of truth

graph.json (id="current")Factor rows it's built from
Written byevery run of update-event-graph, unconditionallythe first successful discovery of that canonical_name
Update rulefull replace — nodes, edges, metrics all recomputedfirst-write-wins on descriptive fields
History keptnone — prior snapshot leaves no tracerow persists indefinitely
First-write-wins?no — nothing is asserted more than onceyes — description & category freeze after first write
stateDiagram-v2
    direction LR
    classDef computeState fill:#161822,stroke:#e0e0e0,stroke-width:2px,color:#f0f0f0;
    [*] --> current: every run of update-event-graph
    current --> current: full overwrite —
nodes, edges, metrics all recomputed class current computeState

The entire lifecycle of graph.json. There is no Stable / Orphaned / Purged here — nothing on this row is ever asserted more than once.

Concept
Example
graph.json holds one row and every field changes on every run — there's no first-write-wins group, because nothing here is ever asserted more than once.
delete + re-run same structure back
Every fact also lives upstream first: node attributes from Event/Factor, edges from EventDependency/EventFactor, and even the two computed fields are functions of data already on the Event rows.
nothing here is the only copy of itself
Contrast the Factor rows the graph is built from: delete factors.json and a factor's description is gone — written by one model call, once.
factors.json deleted descriptions unrecoverable
04

A structural check the market can fail

Concept
Example
A hard depends_on edge is a claim the market's own prices can check: if B must happen before A, A's price should never exceed B's.
P(effect) > P(cause) violation
A violation is provable pricing incoherence, not a judgment call — but the check can't tell noise on a thin market from real disbelief, and isn't meant to.
a sequel priced above its prerequisite
Coherence and the chain / cycle metrics are computed only over the dependency subgraph — events with no depends_on edge are excluded, not counted as singletons.
most events: zero dependency edges
A hard cycle is treated as a data-quality bug — a precondition can't depend on its own consequence.
hard_cycle: true flagged as a bug
Whether a violation was real resolves only when the precondition settles: it either comes true (pricing was noise) or hardens into a provable mispricing.
precondition settles violation resolves or hardens

See also