pub enum GraphOp {
AddNode {
id: Uuid,
kind: String,
name: String,
summary: String,
attrs: Value,
group_id: String,
},
AddEdge {
id: Uuid,
src: Uuid,
dst: Uuid,
rel: String,
fact: String,
attrs: Value,
group_id: String,
valid_at: Option<TimestampMs>,
invalid_at: Option<TimestampMs>,
},
AddEpisode {
id: Uuid,
source: String,
kind: String,
content: String,
occurred_at: TimestampMs,
group_id: String,
mentions: Vec<Uuid>,
},
UpdateNode {
id: Uuid,
name: Option<String>,
summary: Option<String>,
kind: Option<String>,
attrs: Option<Value>,
},
InvalidateEdge {
edge_id: Uuid,
invalid_at: TimestampMs,
},
MergeNodes {
from: Uuid,
into: Uuid,
},
Purge {
ids: Vec<Uuid>,
},
}Expand description
A single append-only write. See module docs.
Variants§
AddNode
Create an entity node.
Fields
id: UuidUUIDv7 identity, minted by the caller (crate::Grit::new_id).
AddEdge
Create a fact edge between two nodes (“fat edge”: the fact sentence lives on the edge).
Fields
valid_at: Option<TimestampMs>Event time the fact became true; None = unknown / always.
invalid_at: Option<TimestampMs>Event time the fact stopped being true, when already known at
creation (e.g. the source text states the end). Unlike
GraphOp::InvalidateEdge this is NOT a belief retraction and
sets no expired_at — the fact was recorded with a bounded
interval from the start.
AddEpisode
Record raw provenance (a chat turn, a document chunk, a sensor-event summary) and link it to the nodes/edges it mentions.
Fields
kind: StringFree-form source-kind tag (e.g. "message", "text",
"json"). Opaque to grit — callers filter on it.
occurred_at: TimestampMsEvent time of the episode.
UpdateNode
Revise a node’s entity metadata (Layer 2 decides the new values —
summary refresh, name/label promotion; grit executes). None fields
are untouched.
Each provided field is a last-writer-wins register: concurrent
updates converge to the highest HLC per field, independently of
apply order (recorded append-only in node_updates, folded into the
node row — the same out-of-order machinery as edge invalidations).
Prior values remain in the oplog; this is metadata revision with an
audit trail, not fact mutation — facts live on edges and stay
append-only (Design Invariant 4).
Deliberately cannot touch identity (id, group_id), lifecycle
(expired_at, merged_into), or anything on edges.
Fields
InvalidateEdge
Close an edge’s event-time interval: the fact stopped being true at
invalid_at. Never deletes; concurrent invalidations converge to the
minimum (earliest) time. The edge row’s expired_at is untouched
(that means full belief retraction in grit’s read model); when the
invalidation itself was learned is edge_invalidations.recorded_at.
Fields
invalid_at: TimestampMsEvent time the fact stopped holding.
MergeNodes
Fold node from into node into. Layer 2 decides whether (an LLM
judgment); grit only executes. Re-points edges and mentions, marks
from expired with a merged_into audit pointer.
Purge
Explicit, audited right-to-forget. The ONLY destructive op: hard-deletes
and permanently tombstones exactly the listed ids (a tombstoned id can
never be re-added, which is what makes purge commute with concurrent
adds). List incident edge ids explicitly — their fact sentences carry
the content being forgotten; crate::Grit::node_history enumerates
them. The purge op itself stays in the oplog as the audit record.