Event Flow
1
Trigger Events
The TM loop polls
system_event for artifact commit outbox events. Each event contains RecordRef entries that are mapped to trigger types: Rel, Attribute, Entity, Assertion, or Predicate.2
Detect Conflicts
For each rel or attribute trigger, the system computes a slot key, loads the slot’s claims, and partitions them into value groups by exact value equality. A single value group is committed directly — either a first claim or a corroboration (multiple sources agreeing), which merges evidence into a winner and soft-deletes the duplicates without closing any validity window. Multiple value groups create or update a conflict group. Detection is deterministic and replay-stable; it never calls an LLM.
3
Resolve Slot
Real conflicts are adjudicated by an LLM that sees the rendered claim values (see below). Deployments without an API key fall back to the predicate’s deterministic conflict policy. Verdicts are memoized per claim-set hash on the conflict group, so an unchanged claim set is never re-adjudicated.
4
Commit
The winning value is written to the truth slot. Superseded claims get their
claim_status updated and their validity windows closed, preferring dates the adjudicator extracted from the claim values over ingest timestamps. If the group’s claim set changed since resolution, the commit no-ops with a warning. An audit log entry carries the rationale.Slot Key Format
A truth slot is uniquely identified by its slot key, which encodes the subject, object, predicate, and security context: For rels (entity-to-entity):security_hash is a deterministic SHA-256 of the security envelope (policy
ID, level, sorted labels, and sorted handling tags). Claims in different
envelopes occupy separate truth slots, so this comparison does not detect
contradictions across those boundaries.
Cross-object conflicts
Because rel slot keys include the object, “Alice works_at ExampleCo” and “Alice works_at ExampleLabs” occupy different rel slots. The rel_set slot exists to catch exactly this: when a subject has more than one distinct object for a predicate whose cardinality is configured asOne — or whose semantics are unknown — a rel_set conflict group is created. Predicates configured with cardinality Many and event-like predicates never produce rel_set groups. Without an adjudicator, rel_set groups resolve as coexist (all values approved) rather than guessing a winner.
LLM Adjudication
WithANTHROPIC_API_KEY set, the pipeline binary constructs a slot adjudicator (TmContext) that is the primary resolution path for real conflicts:
- Model:
PIPELINE_TM_MODEL(defaultclaude-sonnet-4-5) - Prompt:
crates/pipeline/prompts/tm_adjudication.txt - Contract: meerkat structured output with a strict schema and retries; malformed output after retries falls back to the deterministic policy with a warning
- Input: per-claim rendered values, confidences, evidence counts, source timestamps and time basis, plus predicate semantics when known
Verdicts may include
equivalent_groups (same value, different formats); members merge into their group winner like corroboration. When the predicate has no registered semantics, the verdict must propose semantics (cardinality, temporal model, conflict policy), which are persisted to the predicate registry with semantics_status: llm_proposed — never overwriting operator-configured semantics, revisable by later adjudications, and emitting the predicate-registry outbox event so dependent slots recompute.
Event-slot conflicts flow through the same adjudicator. On the event commit path, coexist verdicts approve all ranked values, and the event reject floor applies only to non-adjudicated resolutions.
Fallback Conflict Policies
Without an adjudicator (or when adjudication fails), the predicate’s conflict policy from thepredicate_registry applies:
Keyless deployments never supersede a value unless the predicate has configured semantics that call for it. There are no per-predicate hardcoded defaults; semantics come from the registry (operator-configured or LLM-proposed).
How Assertions Compete
When the TM system detects divergent values, it gathers the competing rels or attributes into a conflict group. Resolution then:- Loads all claims for the slot, including their rendered values.
- Checks the memo: if the claim-set hash matches the group’s
adjudicated_claim_set_hash, the previous verdict stands and no LLM call is made. - Adjudicates (LLM) or applies the conflict policy (fallback) to rank or partition the claims.
- Updates claim statuses: accepted rel/attribute claims get
ClaimStatus::Approved(approvedon the wire), superseded claims getClaimStatus::Superseded, and corroborated duplicates are merged and soft-deleted. Aneeds_reviewoutcome does not declare a winner. - Writes the truth slot with the current value(s), a confidence score, and the rationale.
Recompute
Thetm_recompute work kind refreshes stale truth slots. A slot becomes stale when:
- An entity involved in the slot is merged or deleted
- A rel or attribute is updated or retracted
- Predicate semantics change (including newly learned
llm_proposedsemantics) - A freshness sweep flags the slot for recomputation
tm_resolve_slot work item enqueued per group. This separates model latency from recomputation; it does not guarantee a fixed completion time under every workload.
