Locked facts
Locked Facts
A locked fact is a value Mnemix surfaces to an agent forever — until a higher-priority source supersedes it. It's the primitive that stops drift: the opening balance the agent can't misreport, the package version it can't silently bump, the caller's name it can't forget on turn 40.
If a human confirmed it once, Mnemix surfaces it forever. Forgetting is failure.
What makes a fact "locked"
Three things, all required:
- A value — structured JSON (
{"model":"voyage-4-lite","dims":1024},{"amount":-49.98}). - Evidence — provenance recorded with the fact. You cannot lock a fact without it.
- An address —
entity_id(who/what the fact is about) +fact_key(which fact). That's how the gate and the context packer look it up.
Schema
The substrate table (as shipped in the live migrations):
| Column | Type | Description |
| :--- | :--- | :--- |
| ref_id | uuid | Primary key. |
| tenant_id | uuid | RLS isolation. |
| entity_id | text | What the fact is about (acct_acme, repo_mnemix, a caller id). |
| fact_key | varchar(255) | Fact identifier (embedder.canonical, opening_balance.2025-08-01). |
| fact_value | jsonb | The fact data. |
| valid_range / tx_range | tstzrange | The bi-temporal windows — when it was true, and when Mnemix believed it. GiST-indexed. |
| supersedes_ref_id | uuid | Self-reference to the fact this one replaced. |
Writing facts — supersession, never UPDATE
Facts are written through supersession-safe stored procedures — assert_fact (new fact) and evolve_fact (replace a value while preserving history). When a fact changes, the substrate does not UPDATE the value:
- Bound the old fact — close its
valid_rangeat the supersession moment. - Insert the new fact — its
valid_rangeopens where the old one closed. - Link them — the new row's
supersedes_ref_idpoints at the old row.
test_runner = "jest" valid: [2025-01-01, 2026-05-29) ◀── superseded by ─┐
test_runner = "vitest" valid: [2026-05-29, ∞) ← active │
supersedes_ref_id ───────────┘
This guarantees any historical audit can reconstruct the exact state of the world at a moment:
SELECT fact_value FROM locked_facts
WHERE entity_id = 'repo_mnemix' AND fact_key = 'test_runner'
AND valid_range @> '2026-03-01T00:00:00Z'::timestamptz;
-- → "jest" (what was true in March, even though it's "vitest" now)
See Bi-temporal Validity for the full time model.
Locked facts surface in agent context
The context packer is designed to inject a LOCKED FACTS YOU MUST HONOR block into agent context for the relevant entities (Weld A — the mechanism that makes remembering automatic). The agent sees test_runner = vitest every turn; when it proposes import { describe } from "jest", the gate's test_runner_consistency rule blocks it.
Facts over the API
There is no public facts REST surface yet — fact writes go through the substrate's stored procedures via the ingestion paths, and fact reads surface inside context packets. A direct facts API (lock / resolve / audit-chain) is on the beta roadmap; the docs will gain real request/response examples when it ships. Any older draft showing facts CLI subcommands or facts REST calls predates this correction and doesn't exist.
See also
- Evidence Refs — what every locked fact must cite.
- Policy Bundles — the rules that read locked facts.
- The Validation Gate — what blocks an action that contradicts a fact.