skip to content
TJ Miller

Introducing Truths

/ 8 min read

I was updating the documentation for Iris’s memory system when something clicked. I’d been staring at the two-tier architecture diagram, writing up how Tier 1 “important memories” always get included in context, and it hit me:

Importance and universality are not the same thing.

The whole Tier 1 system was fundamentally flawed. Let me explain.

The Flaw I Finally Saw

Iris has been using a two-tier memory system:

  • Tier 1: Memories with importance ≥ 0.80 always included in every conversation (max 5)
  • Tier 2: Semantic search retrieves contextually relevant memories based on conversation

The assumption was straightforward - important memories should always be available. Except… that’s not quite right, is it?

A memory can be critically important but still episodic or contextual. “User’s mother was diagnosed with cancer last month” might score 0.95 importance, but it’s not relevant when you’re debugging a Laravel controller. Meanwhile, “User’s name is TJ” might get a modest importance score but should genuinely be available in every conversation.

Here’s the thing: the old tier system was trying to use a single dimension (importance) to solve a two-dimensional problem (importance AND universality). We were conflating two completely different questions:

  1. How significant is this information? (importance)
  2. Should this be available regardless of topic? (universality)

High importance doesn’t mean universally relevant. Low importance doesn’t mean contextually irrelevant. They’re orthogonal.

Once I saw it, I couldn’t unsee it.

Enter Truths

So what I ended up doing was scrapping the importance-based tier system and replacing it with something called Truths.

Truths are a separate layer entirely. Different model, different lifecycle, no expiration. They represent distilled, stable facts that are relevant across conversations. Think of the difference this way:

  • Memories are observations. They capture what happened, what you said, what you prefer in a given context. They’re raw material.
  • Truths are conclusions. They’re stable facts distilled from patterns in those observations over time.

”User mentioned preferring morning meetings three times across different conversations about scheduling” is observational data (memories). “User prefers morning meetings” is the conclusion (a Truth).

The key insight: instead of guessing which observations deserve “always available” status at extraction time, we let actual usage patterns determine what earns Truth status. If a memory keeps getting retrieved across different conversation contexts, that’s the signal. Not an LLM guessing “this seems important.”

Evidence Over Prediction

This is really the philosophical shift at the heart of Truths.

The old system asked an LLM to predict the future: “Will this memory be relevant across many conversations?” That’s a hard question. The LLM has no idea what conversations you’ll have next week. So it falls back on heuristics - names seem important, health information seems important, work details seem important. Sometimes those heuristics are right. Often they’re not.

The new system asks a much simpler question: “Has this memory already proven useful across many conversations?” That’s not prediction. That’s observation. We’re not asking the LLM to guess. We’re showing it evidence and asking it to classify.

This is a pattern I keep coming back to: defer decisions until you have data. Don’t try to predict importance at creation time. Collect behavioral evidence, then promote what earned it.

The Distillation Process

The nightly distillation job (iris:distill-truths) evaluates memories based on behavioral evidence using a weighted scoring system:

FactorMax PointsCalculation
Access frequency40min(40, access_count × 4)
Access recency20max(0, 20 - (days_since_access × 2))
Consolidation generation20min(20, generation × 10)
Context diversity20min(20, access_count × 2)

A memory needs a score ≥ 50 with at least 5 accesses to become a promotion candidate. The system then filters to the top 10th percentile by access count, requiring a minimum of 20 total memories before distillation even runs.

Once we have candidates, Claude evaluates each one - but now it’s answering an easier question: “Is this a stable, generalizable fact or an episodic event?”

Promotable: “User prefers TypeScript over JavaScript” - stable preference that applies across contexts.

Not promotable: “User was debugging a TypeScript error yesterday” - temporal event that will become irrelevant.

The difference is we’re not asking the LLM to predict universality anymore. We’re showing it evidence of what actually proved universal and asking it to validate.

Truth Sources

Truths can come from three places:

SourceDescription
promotedDistilled from memories that proved consistently useful
user_createdManually added through the UI
agentCreated by Iris during conversation when identifying core facts

Each source gets different treatment. Promoted Truths can be crystallized (refined with new evidence). User-created and agent Truths are protected from automatic modification - your explicit intent is preserved.

Contextual Re-ranking

Here’s something else that bugged me about Tier 1: everything was always included, period. Your coffee preference showed up when debugging production issues. Partner details appeared when writing documentation.

With Truths, even “core” facts get ranked by relevance to the current conversation. The retrieval flow:

  1. Pinned Truths are always included (no limit)
  2. Remaining Truths are scored by semantic similarity to the current message
  3. Top N dynamic Truths are included (default: 7, configurable via truths.max_dynamic)
  4. Similarity threshold filters out anything below 0.15

Your name? Usually relevant. Your coffee order? Maybe not when you’re knee-deep in database migrations.

This feels a lot cleaner. You get the stability of “always available” facts without irrelevant noise eating your context window.

Pinning

Sometimes you genuinely want something in every conversation, no matter what. That’s what pinning is for.

Pinned Truths bypass relevance scoring entirely. Pin 20 Truths, all 20 are included. Use this thoughtfully since it affects your context budget, but it’s there when you need it.

You can also create Truths directly - through the UI or during conversation. Tell Iris “Remember that I’m allergic to shellfish - that’s important” and it’ll create a Truth rather than a regular memory.

The key principle: user explicit choice always wins.

Crystallization

And honestly? This part is what I’m most excited about.

Human memory doesn’t work like a database. You don’t just store facts and retrieve them unchanged forever. Your understanding of things refines over time as you gather more evidence, more context, more nuance. Your mental model of a friend sharpens as you learn more about them. Your sense of your own preferences clarifies through experience.

Truths work the same way. When new evidence supports an existing Truth, it gets crystallized - refined to incorporate the new information while keeping its core meaning. The system tracks crystallization_count and last_crystallized_at for each Truth.

Say you have a Truth: “Works as a software developer”

Over the next few months, conversations reveal you’re specifically a senior backend engineer at a fintech company. Crystallization refines that Truth to be more precise without you manually updating it. The Truth didn’t change - it got sharper.

It’s like the Truth is growing more crystalline and well-defined as evidence accumulates. Hence the name.

This matters because it means Truths aren’t static snapshots. They evolve organically as Iris learns more about you. The goal isn’t to capture a fact once and preserve it forever - it’s to maintain an accurate, current understanding that grows more precise over time.

Crystallization only applies to promoted Truths that aren’t pinned. The similarity threshold for triggering refinement is 0.85 - high enough to ensure the new evidence is actually about the same topic. User-created Truths are protected from automatic modification because your explicit intent should be preserved.

Truth Tools

Iris has four tools for managing Truths during conversation:

ToolPurpose
store_truthCreate a new Truth when identifying core facts
search_truthsFind existing Truths by semantic similarity
update_truthModify a Truth’s content
delete_truthRemove a Truth

These mirror the memory tools but operate on the Truths layer. When you tell Iris something feels important enough to always remember, it can create a Truth directly rather than waiting for distillation.

Full Visibility

One of the core tenets of Iris is visibility. You shouldn’t have to wonder what your AI knows about you or trust a black box. So naturally, the Truths system gets a full management UI.

The Truths page gives you complete control:

  • Search and filter by category, source, and pinned status
  • Each Truth card shows content, category, source label, access count, and crystallization count (with a 💎 indicator for refined Truths)
  • Pin/unpin, edit, or delete any Truth directly
  • Stats panel showing total Truths, pinned count, average accesses, and stale count (Truths not accessed in 90+ days)

The Insights dashboard adds analytics:

  • Source distribution donut chart - see where your Truths come from
  • Category breakdown across all eight memory types
  • Crystallization stats - how many Truths have been refined, total refinements, average refinement count, and which ones evolve most
  • Distillation score histogram showing the behavioral evidence behind promoted Truths
  • Most accessed Truths and recently added Truths

The goal is transparency. If a Truth seems wrong or irrelevant, you can see exactly how it got there and what its usage patterns look like. Then you can fix it.

No black boxes.

The Takeaway

Sometimes you’re too close to a system to see its flaws. I’d been looking at that two-tier architecture for months, assuming the problem was tuning - maybe the 0.80 threshold was wrong, maybe the importance scoring needed adjustment.

Turns out the whole framing was off. We were using importance as a proxy for universality, and they’re fundamentally different things.

Truths split those dimensions properly:

  • Memories capture observations with importance scores. They’re contextually retrieved based on what you’re discussing.
  • Truths represent stable conclusions earned through behavioral evidence. They’re re-ranked per conversation but always available as candidates.
  • Pinning gives you explicit control for the handful of facts that genuinely need to be in every conversation.

The conceptual model now matches how knowledge actually works. You observe things (memories), patterns emerge (distillation), conclusions form (Truths), and those conclusions refine over time (crystallization). The AI isn’t guessing what matters. It’s watching what proves useful and promoting accordingly.

That’s the shift: from prediction to evidence. From guessing to observing. From static importance to earned universality.

For the full technical details, check out the Truths documentation.

Subscribe

For more thoughts, links, project and personal updates.