pub const CREATE_LINKS_LOG_INSERT: &str = r#"
CREATE TRIGGER IF NOT EXISTS trg_links_log_insert
AFTER INSERT ON links
BEGIN
INSERT INTO transaction_log (table_name, entity_id, operation, payload, recorded_at, branch_id)
VALUES ('links',
NEW.source_id || '|' || NEW.target_id || '|' || NEW.edge_type || '|' || NEW.valid_from,
'I',
json_object('v', 1, 'source_id', NEW.source_id, 'target_id', NEW.target_id,
'edge_type', NEW.edge_type, 'valid_from', NEW.valid_from,
'valid_to', NEW.valid_to, 'weight', NEW.weight,
'properties', json(NEW.properties)),
NEW.recorded_at, NEW.branch_id);
END;
"#;Expand description
The links log, and the entry whose entity_id is composed rather than copied.
source|target|type|valid_from identifies an edge assertion and carries no
lineage, which is why branch_id had to become a column of its own rather
than a fifth field in that string. Re-keying entity_id was the other
option and was rejected: it changes what a log entry identifies, so rows
written before the rung would no longer match rows written after it, and the
fold would silently split one edge’s history in two.
With the column present, the four folds in temporal::replay — a private
module, so the name is plain text rather than a link that would not resolve —
partition by (table_name, entity_id, branch_id) and two lineages’
assertions about one edge stay two beliefs. Without it they collapse to
whichever has the higher seq_id — no error, no drift report, just one
lineage’s belief gone.