How it works

Three gears, and the order matters. A language model reads your notes and suggests facts — that is the only thing it is allowed to do. A small deterministic engine keeps every fact with the dates it was true, so nothing is ever overwritten — when the owner changes, the old owner becomes history with an end date, not garbage. And before any action goes out, the gate checks each thing the action assumes against those dates.

Expired → blocked, with the exact line that replaced it. Contradicted → blocked, with both sides shown. Never recorded → “no basis to approve”, because silence is not agreement. Still true → allowed, with receipts.

The three answers

Most systems can say a value, or say “I don’t know.” The product is the third answer: I knew this, and it expired. A system that cannot tell “never knew” from “knew, and it stopped being true” will act on expired facts with full confidence — and nothing about its output will look wrong.

Why the truth part isn’t a model

Whether one fact supersedes another is decided by four rules in tested code, not by sampling. Same input, same output, every run — so a wrong answer here is a bug that gets a regression test, not a temperature setting. The engine never reads a clock either: “now” is an argument the caller must pass, which is why a log entry dated next week doesn’t count as true today.

Engineering detail — what’s deterministic, what I cut, what it still gets wrong

What the model is allowed to do

Nominate candidate observations from text, and nothing else. It never decides what is true. Every candidate is schema-validated before the engine sees it, because schema-constrained output is a strong hint, not a guarantee — malformed candidates are dropped, not coerced.

What is deterministic

  • Supersede chains, conflict detection, historical backfill.
  • All temporal queries — now, as-of, and the boundary rules.
  • Premise verdicts and whether a draft is blocked.

The engine is pure: no clock, no randomness, no network. Same input, same output, every run — which is why the whole thing is testable and replayable, and why a wrong answer is a bug rather than a temperature setting.

Three answers, not two

Most systems can say a value or say they don’t know. The third answer is the product: I had evidence and it is no longer usable — stale, or contradicted. A system that cannot tell “never knew” from “knew, and it expired” will act on expired facts with full confidence, and nothing about that output looks like a hallucination.

Dates

ISO YYYY-MM-DD strings everywhere, never Date objects. Lexicographic comparison equals chronological comparison for that format, so there is no date library and no timezone that can shift a fact onto the wrong day. Intervals are half-open [validFrom, validUntil): a value replaced on the 28th is not also true on the 28th.

Cuts I made deliberately

  • No entity resolution. “Atlas” and “Project Atlas” are the same string after normalisation, but nothing fuzzy-matches. Wrong merges corrupt truth state silently, and a silent corruption is worse than a missed link.
  • Conflicts are not auto-resolved. No source-priority heuristic, no recency tiebreak on same-day claims. The evidence does not order them, so neither does the engine.
  • No persistence. State lives in one reducer for the session. The engine is a pure function over a fact table, so a database is a storage decision, not a design one.
  • Premise extraction is single-turn. One question, one set of triples. No multi-hop reasoning over chained assumptions.

Known limitations

  • Observation date is taken from the line, so it conflates “when it was recorded” with “when it became true”. A full bitemporal model separates those; this one collapses them, which is fine for timelines and wrong for backdated records.
  • Corroboration is a count, not a weight. Ten copies of one rumour outrank one primary source.
  • A property that is genuinely multi-valued (two owners at once) reads as a conflict.

See it run →