Designing a Reasoning Ledger Record

A companion to Part 4 of the Building the AI Memory Stack series. Part 4.5 of the series.

Part 4 argued that agentic systems need a Reasoning Ledger: a layer that preserves why a decision happened, not just what was decided. The comment thread that followed turned into something more specific and more useful, a working design conversation about what a single ledger record should actually contain. This piece consolidates that. Several of the strongest ideas below arrived from other people, and I have tried to credit them where they land.


The easy version of this article is a schema. Here are the fields, copy them, done.

I want to resist that, because the field list is the least durable thing I could hand you. Implementations differ, field names drift, and a record shape copied without its reasoning becomes cargo-cult structure that nobody maintains. The useful thing is the set of design tensions that decide what belongs in the record and what does not. Get those right and you can derive the fields yourself. Get them wrong and no schema will save you.

So this is principles first, record second. At the end there is a worked record and a field reference, tagged for what is core and what is genuinely optional.

A Starting Point

Here is the baseline record from Part 4. It is a reasonable start and, as the thread quickly established, incomplete in instructive ways.

reasoning_ledger:
  decision: "Approve deployment"
  timestamp: 2026-03-14T09:22:00Z
  evidence:
    - artifact: ADR-014
      authority: architecture-review
      version: 3
    - artifact: security-policy
      authority: security-team
      version: 7
  tools:
    - GitHub
    - CI pipeline
  approvals:
    - release manager
  outcome: approved

Every principle below is, in effect, a thing this record does not yet say.

Principle 1: The Ledger Witnesses, It Does Not Enforce

The first tension is architectural, and it is the one I would defend hardest. A reasoning ledger must not be able to block, veto, or gate the action it records. Its job is to preserve what happened and what evidence surrounded it. The moment the ledger can prevent an action, it stops being an independent witness and becomes part of the mechanism it is supposed to describe, and its own records stop being examinable as neutral fact.

This came up when pm25coder noted, correctly, that a ledger that only narrates can quietly become fiction, and that trust comes from being able to gate rather than merely describe. I agree with the diagnosis and draw the boundary one step earlier: enforcement is real and necessary, but it belongs at the policy and tool boundary, not inside the witness. The ledger preserves that the boundary was evaluated and what it returned. The boundary decides whether the action proceeds.

The practical consequence for the record: a ledger entry can contain a policy_evaluated result showing that a check ran and what it concluded, but it never contains the enforcement decision as its own authority. It reports; it does not rule.

Core. This is not a field, it is a constraint on the whole design.

Principle 2: Supersession Is a New Event, Never a Rewrite

A superseded decision should become a new record that points back at the old one. It should never overwrite the original. “We decided A, and later decided B instead” is two events with a relationship between them, not one field that changed value.

This matters because “wrong now” does not mean “was never decided then.” If you rewrite the March record when you change course in August, you have destroyed the ability to answer whether the March decision was reasonable given what was known in March. The noisier history is the correct trade. Compaction can always produce a clean current-state projection later, but once you have rewritten the historical evidence, you cannot reconstruct it.

This is the same append-only discipline that makes Forensic Receipts useful: preserve what was decided under which evidence and authority, then record the superseding decision as its own event with its own receipt.

Core.

Principle 3: Record How the Authority Was Obtained, Not Just Which One

The baseline record says version: 7. That tells a future reader what supposedly governed. It does not tell them how the system established that version 7 was authoritative at decision time, and those are very different trust claims.

Self-Correcting Systems and pm25coder arrived at this from opposite directions and met in the middle: a policy version fetched fresh from its authority at 09:22, a version read from a five-minute cache, and a version inherited from session state can produce identical version: 7 fields while supporting completely different claims about what the system could reasonably have known. The fix is to treat the authority fetch itself as a recorded event. The record should say which source was consulted, when, what came back, and whether cached state was involved.

This also exposes the sharpest failure mode in the thread, the one an otherwise perfect ledger cannot catch on its own. If the external authority moved to version 8 an hour before your decision and nothing in your system observed that change, the record faithfully captures version 7 and stays perfectly self-consistent. It is a flawless account of a decision that was already wrong when it was made. The record cannot flag this, because there is no edge to preserve; nothing inside the system ever saw the change. Recording how the version was obtained at least lets a later examiner distinguish “we checked and got stale data” from “we never checked.”

Core for the fact of how evidence was obtained. The revalidation mechanism that catches silent version drift lives outside the record, and Principle 7 covers it.

Principle 4: Relationships Need Two Clocks

If you ever want to reconstruct what the system could have known at a past moment, every relationship in the ledger needs two timestamps, not one. This is standard bitemporal modeling, and Giulio D’Erme named exactly why it is not optional here.

Valid time is when a fact was true in the world. Transaction time is when your system asserted or learned the relationship. If a supersession edge carries only a single date, replaying last March will show March’s decision annotated with August’s supersessions, and the decision-maker will look like they ignored a policy that did not yet exist. You will have judged a past decision using knowledge that arrived in the future, which is the precise thing a reasoning ledger exists to prevent.

So a supersession or correction relationship carries both valid_time (when the new state became true) and asserted_at (when the system recorded the edge). Reconstruction filters on asserted_at to see only what was knowable then.

Core for any ledger whose purpose includes reconstructing historical decision context. If you genuinely only ever query current state, you can defer this, but that is a smaller ambition than most of these systems have.

Principle 5: Preserve What Lost, Not Just What Won

A ledger that records only the evidence supporting the final decision is a post-hoc justification engine wearing an audit trail. You can reconstruct why the decision looked reasonable, and you have quietly lost what competed with it, what failed a threshold, and what stayed unresolved.

GnomeMan4201 made this case from the investigation side, and it reframed the record for me. An immutable ledger can preserve history perfectly and still preserve a biased history if the losing evidence never gets written. The distinction between “we chose A because of X” and “we chose A because of X, rejected B because of Y, and could not resolve Z” is enormous when someone later asks whether the decision was defensible given what was actually known.

The fields this implies: alternatives_considered with a rejection_reason for each, disconfirmed_by for evidence that actively cut against the chosen path, and unknowns or scope_limitations for what the system could not resolve at decision time.

A scoping note, in answer to Kartik N V J K, who asked whether to capture rejected branches: capture the alternatives that were explicit parts of the decision process, not an exhaustive reconstruction of every path the model internally considered. If the agent evaluated three tools and rejected two on policy grounds, those rejections are observable decision evidence and belong in the record. The model’s private deliberation does not. Observable reasoning is architecture; private reasoning belongs to the model.

Optional, escalating to Core with stakes. For a low-consequence decision, surviving evidence may be enough. For anything a human will later audit, defend, or be held accountable for, treat these as required. The higher the stakes, the more the losing evidence matters.

Principle 6: The Trigger Is a First-Class Field

pm25coder offered the most immediately practical field in the thread, from running a live decision ledger: the thing people actually read first, months later, is not the outcome. It is what provoked the decision. A timestamped complaint, an incident, a threshold breach, a human request. When every record carries its trigger, “why did we change this” becomes a search rather than an archaeology project, and the audit trail starts writing itself.

It is easy to bury the trigger inside an evidence list. Do not. Promote it to its own field, because it is the field that makes the record findable by the question a future reader will actually bring to it.

Core. Small field, disproportionate value.

Principle 7: Some Things Belong Outside the Record

Two mechanisms the thread kept reaching for are real and necessary, and they do not go in the ledger entry. Naming them keeps the record honest about what it is.

The first is revalidation. A ledger cannot observe a change in the outside world that never entered the system, so something outside the ledger has to periodically re-fetch referenced authorities and emit a fresh observation. pm25coder described this as a periodic “still current” or “stale” marker, which is a clean way to put it. The important framing: the revalidation job runs outside the ledger, and its result becomes a new event the ledger preserves. The ledger never claims continuous authority between checks, only that authority was observed at particular moments.

The second is retrieval. Giulio D’Erme and arun rajkumar converged on the point that a ledger gets read at exactly one moment, when someone is about to change the thing the reasoning was about, and that nobody goes looking for a constraint they have never hit. A well-structured record that is never surfaced is not much better than no record. The fix is to make the decision history an obligation on retrieval rather than an obligation on the reader: when a query surfaces the artifact a decision governed, the decision rides along, asked for or not.

That is what I have started calling separate custody, one interface. The ledger stays independently governed, so it cannot be edited in the same operation that changes what it witnesses. But the retrieval layer reunites the artifact and its decision history when the relationship becomes relevant, so no one has to know the ledger exists to benefit from it. Both properties matter, and they pull in opposite directions, which is exactly why they belong to different layers.

Core as principles, external as mechanisms. Neither is a field in the record.

A Worked Record

Applying the core principles to the baseline, a fuller record looks closer to this. The optional fields from Principle 5 are included and marked, since this is the kind of consequential decision where they earn their place.

reasoning_ledger:
  decision_id: dep-2026-03-14-0922
  decision: "Approve deployment"
  decided_at: 2026-03-14T09:22:00Z

  trigger:                                 # Principle 6
    type: incident
    ref: INC-2291
    observed_at: 2026-03-14T08:55:00Z

  evidence:
    - artifact: ADR-014
      authority: architecture-review
      version: 3
      obtained:                            # Principle 3
        source: adr-service
        method: re-derived
        retrieved_at: 2026-03-14T09:21:40Z
    - artifact: security-policy
      authority: security-team
      version: 7
      obtained:
        source: policy-cache
        method: cached
        retrieved_at: 2026-03-14T09:21:41Z
        cache_age_seconds: 240

  policy_evaluated:                        # Principle 1 (reports, does not rule)
    - check: dirty-tree-guard
      result: pass

  alternatives_considered:                 # Principle 5 (optional, stakes-dependent)
    - option: "Defer to next window"
      rejection_reason: "Incident severity exceeded defer threshold"
  disconfirmed_by: []
  unknowns:
    - "Downstream cache warm state not verified"

  relationships:                           # Principle 4 (two clocks)
    - type: supersedes
      target: dep-2026-02-02-1130
      valid_time: 2026-03-14T09:22:00Z
      asserted_at: 2026-03-14T09:22:00Z

  approvals:
    - release-manager
  outcome: approved

Field Reference

For quick use, here is the same thing as a reference, tagged.

Core fields. decision_id, decision, decided_at, trigger, evidence (with per-item authority, version, and an obtained block recording source, method, and retrieval time), outcome, and, for any relationship, both valid_time and asserted_at.

Optional fields, escalating to core with stakes. alternatives_considered with rejection_reason, disconfirmed_by, unknowns, scope_limitations.

Optional, context-dependent. confidence assessments, tools used, and policy_evaluated results where a boundary check ran. Useful, but not every decision needs them, and an empty one is worse than an absent one.

Not fields at all. Enforcement decisions, revalidation jobs, and integrity guarantees. These are mechanisms that surround the ledger, not contents of the record.

The Honest Limit

It is worth ending where the design genuinely runs out, because pretending otherwise is how ledgers get oversold.

A perfect record can tell you exactly what the system knew and did. It cannot retroactively give the system knowledge it never acquired. If the world changed and no observation of that change ever crossed your boundary, the ledger will contain a flawless, self-consistent account of a decision that was already wrong. Revalidation narrows that gap. It does not close it. Auditability is a property of what was observed, not a guarantee that everything relevant was.

That is not a reason to skip the record. It is a reason to be precise about what the record proves. It witnesses observation, not omniscience.

Looking Ahead

This piece is about what a record should contain and the principles that decide it. It has deliberately said almost nothing about whether the record can be trusted not to have been altered after the fact. That is a separate problem with its own answer, Write-Side Custody, and it is where Part 5 goes next. Designing the record and guaranteeing its integrity are different jobs, and keeping them apart is itself one of the design principles.


With thanks to the commenters whose contributions shaped this: GnomeMan4201 on disconfirming evidence, pm25coder on the trigger field and authority-fetch-as-event, Giulio D’Erme on two clocks and retrieval as an obligation, Self-Correcting Systems on provenance of the version, arun rajkumar on where the record lives, Tae Kim on evidence chains under audit, and Kartik N V J K on rejected branches. The record is better for the argument.

Facebooktwitterredditlinkedinmail

Your Memory API Is Lying to Your Agent

The memory store may know the truth. The interface may be throwing it away.

This piece grew out of a conversation on Edward Izgorodin’s post Agent Memory: Everything It Remembers Has the Same Authority, and That Is the Bug. Several of the sharpest points below have names attached, and I have tried to attach them.


Imagine an AI agent asks its memory system a straightforward question:

What database does the production application use?

The memory API returns:

[
  {"content": "The production database is PostgreSQL.", "score": 0.94},
  {"content": "The production database is MongoDB.", "score": 0.91}
]

Retrieval worked. It found two highly relevant memories, scored them, ranked them, and returned them. The agent picks PostgreSQL.

The production application migrated to MongoDB four months ago.

Nothing failed in retrieval. The PostgreSQL record may genuinely be more semantically similar to the query. But semantic relevance was never the question the agent needed answered. The store knew more than it returned: PostgreSQL governed from January 2025 until April 2026, when MongoDB superseded it under a newer architecture decision. Somewhere between storage and the agent, that relationship disappeared.

Diagram showing a PostgreSQL record valid from January 2025 to April 2026 under authority ADR-017, superseded by a MongoDB record valid from April 2026 to present under ADR-042. The supersession relationship is what a ranked list discards.

The API returned the records and threw away the relationship between them. That is a very different kind of memory failure, and it is the one this piece is about.

The Storage Problem Is Mostly Solved

Before going further, it is worth being honest about what is actually new here, because part of this problem was solved before agents existed.

Separating when a fact was true from when the system learned it is bitemporal modeling, standardized in SQL:2011 as application-time and system-versioned tables. Edward raised this in the thread, and he is right that the database world has handled “this was true then, this is true now” for over a decade. A well-built store can close a fact’s validity window instead of overwriting it, and the past stays explicable.

So the interesting problem is not storage. If your store still deletes on update, fix that first, and the literature is waiting for you. The problem this piece is about starts one layer up: even when the store preserves all of it, the retrieval interface usually hands the agent a flat ranked list and throws the structure away. The store solved the problem. The API un-solves it on the way out.

A Ranked List Has Nowhere to Put an Edge

That phrase is Edward’s, from the thread, and it may be the sentence that breaks the whole abstraction. Once you sit with it, the rest follows.

Most AI memory interfaces inherited a familiar retrieval shape: give the system a query, get back a ranked list of relevant things. There may be metadata attached, a timestamp, a document id, a source, a confidence value. The fundamental abstraction stays the same. Memory is a bag of items, and retrieval returns the best-matching items.

That works well when the problem is finding things. Agentic systems increasingly need memory to do something harder: represent what the system currently knows, what it previously knew, where that knowledge came from, whether it still governs, and how apparently contradictory records relate. A ranked list is a poor representation of that world, because the relationships between records are part of the knowledge, and a list has nowhere to put them.

Consider two records: customer refunds require manager approval, and customer refunds under $100 do not. Maybe the second is a correction, because the first was entered wrong. Maybe it superseded the first, because policy changed. Maybe both are true in different jurisdictions and the first simply no longer governs this transaction. Those are not variations of one operation. They make different claims about history.

Diagram showing one record, A, related to a later record or authority in three distinct ways: superseded by B because the world changed, corrected by B because the record was wrong, and invalidated by an authority because A may still be true but no longer governs.

At the storage layer, all three can look like an update. At the audit and retrieval layers, they are fundamentally different events.

“No Longer True” Is Not “Never True,” and Neither Is “No Longer Governs”

CRUD trained us to think in one verb, UPDATE, but durable memory needs at least three, and the third is the one that gets missed.

Supersession says the world changed. Policy A was true, Policy B is true now, and A is not wrong, it is closed. Correction says our record was wrong, including during the window an agent may have relied on it, so A was never true. Invalidation is the one worth slowing down for, because it is not a truth claim at all. It is an authority claim. A record can be perfectly true and no longer govern.

That distinction is the load-bearing one. A store that collapses these into a single value change can still answer “what is true now” cleanly, and will quietly fail the moment anyone asks “why did the agent approve that transaction on March 17.” The answer to that question may depend on a record that is closed, or corrected, or stripped of authority, and that store no longer knows which.

Availability Is Not Usage, Even for a Schema

Here is the part that should make anyone building this check their own system before writing another feature.

Giulio D’Erme read the original thread, then went and counted his own corpus: zero of 152 memos in his memory store, and zero of 59 documents in his docs, declared a validity window or a supersession edge. The engine could read those keys. Nothing that wrote memories ever wrote them. As he put it, availability is not usage, and it applies to schema as much as to tools.

This is the failure mode hiding behind every rich schema. You can ship the read path, document the fields, and watch a live API serve a dead feature, because the thing that writes memories, a prompt or a template or another agent, was never taught the keys. A supersession column that nothing populates is not preservation. It is a column.

Tae Kim described the same shape from production trade data: the same company surfacing as different nodes depending on whether you asked before or after an acquisition, with the store silently picking one. Stamping the connections with time ranges and returning both versions helped. The part that bit later was that the agent’s choice between them still vanished without a trace, which is the next problem.

Relevance Is Not Authority

The PostgreSQL example exposes the assumption underneath ranked retrieval. A similarity score answers, roughly, “how relevant is this record to the query.” It does not answer “which record currently governs.” Those correlate, but they are not the same. PostgreSQL might score 0.94 because it contains the exact terminology in the query, while MongoDB scores 0.91 because the migration decision is phrased differently. Retrieval did its job. The agent still gets the wrong answer, because 0.94 > 0.91 quietly became conflict resolution, and semantic similarity never established anything about authority.

This is why I have come to think of Memory as Infrastructure rather than memory as a database feature. Once memory participates in consequential decisions, retrieval quality is only one property of the subsystem. Provenance, authority, lifecycle, temporal validity, and correction semantics matter too. The closest memory is not necessarily the memory that governs.

Contradiction Is Information

Memory systems often treat conflicting records as a retrieval-quality problem: delete the older one, rank the newer one higher, filter one out with metadata. Sometimes that is right. Sometimes the contradiction is the most important thing memory knows.

Consider a record from Procurement saying Supplier X is approved for regulated workloads, and one from Security saying Supplier X is prohibited. Both may be inside their validity windows. No supersession may exist. The correct response is not to silently decide which wins. It is to report that the records conflict, where each came from, which authority issued each, and that resolution is required.

Diagram showing two records about Supplier X, one from Procurement marking it approved for regulated workloads and one from Security marking it prohibited, both flowing into a single unresolved conflict node rather than one silently winning.

If the store knows the conflict exists but the API returns two ordinary ranked hits, the disagreement disappears at exactly the moment it mattered most.

The Response Type Is Part of the Architecture

This is why the fix is harder than adding a metadata column. If memory contains relationships, the response type has to be able to carry relationships. A richer interface might conceptually return something like:

{
  "records": [
    {"id": "A", "content": "Production uses PostgreSQL."},
    {"id": "B", "content": "Production uses MongoDB."}
  ],
  "relationships": [
    {
      "type": "supersession",
      "from": "A",
      "to": "B",
      "effective_at": "2026-04-15T00:00:00Z"
    }
  ]
}

The precise schema is not the point, and I am not proposing that JSON as a standard. The conceptual change is that the response is no longer a list of memories. It is a representation of a knowledge state, one that can carry contradiction, supersession, correction, invalidation, provenance, and authority as first-class content. Once those relationships affect agent behavior, they cannot stay trapped in the storage layer.

Two honest problems come with that, and both surfaced in the thread and then got worse the more Edward and I pushed on them.

The first is budget, and it turns out to be deeper than allocation. A ranked list is impoverished, but it is cheap, and top_k is a clean way to decide what to drop. The moment a response carries facts, relationships, authority, provenance, and prior decisions together, the problem stops being ranking and becomes allocating a finite context budget across different kinds of knowledge. A lower-ranked authority edge may matter more than the next highly relevant fact, and dropping a supersession relationship can change the meaning of the records that survive.

The tempting fix is to select the edges after ranking, as a post-filter on whatever top_k returned. Edward’s counter is the part that reshaped my thinking: to know whether a supersession edge is worth carrying, you already have to be holding the record it supersedes. Edge hydration therefore cannot be a post-filter. It has to influence which candidates are considered in the first place, which means the allocation happens before ranking rather than after it. That is a far deeper change to a retrieval stack than adding a field to a response, and it is the point at which “improve the store” stops being the fix.

The second is addressing, and it needs to be more precise than “give the conflict an identity.” My first instinct was to key the disagreement on the pair, A conflicts with B. Edward’s refinement is better: pairs are unstable, because the moment a third record arrives, “A conflicts with B” is no longer the same object, and yesterday’s decision now points at a conflict that no longer exists in that shape. Key on the subject the records argue about instead, the question, not the pair, and the decision stays addressable however many records pile up under it over time.

What Did the Agent Do Last Time?

That second problem points at a relationship that matters once agents repeatedly hit the same knowledge. Suppose yesterday’s agent encountered records A and B in conflict, determined that B governed because Security had authority over regulated workloads, and acted on B. Today another agent hits the same conflict. If the system stored only A and B, today’s agent resolves it from scratch. If yesterday’s decision lives only in an audit log somewhere else, it exists but is unavailable at the moment it could prevent a repeat.

This is where a Reasoning Ledger becomes operationally interesting, and where I want to hold a line rather than blur one. I still think durable memory and the decision record deserve different custody. Knowledge can be superseded; a decision record cannot, because it has to keep saying what was believed at the time even after the belief is retracted. That separation belongs at the storage layer.

It should not survive into retrieval. Mike Czerwinski put the risk plainly in the thread: if the agent’s choice between conflicting records is not logged, silent resolution just relocates from the store to the inference step, the same bug at a harder-to-find address, because now the store looks honest. Tae Kim started writing those choices back as events only because a client asked about a strange output and there was nothing to point at. Audit pressure, not architecture taste, is usually what makes the field real.

So the shape I would argue for is not memory + ledger presented as two things. It is separate systems of record behind one interface that can return facts, relationships, authority, and relevant prior decisions together. Separate custody, one interface, is the shortest way I have found to say it.

Diagram showing five separate subsystems, durable memory, reasoning ledger, provenance, authority and policy, and temporal state, all feeding a single memory and context interface that then serves the agent, illustrating that separate storage boundaries can sit behind one unified retrieval interface.

Different subsystems may have very different storage requirements, retention policies, and security boundaries. The mistake is assuming those implementation boundaries must decide what the agent is allowed to know at retrieval time. Storage boundaries do not have to be retrieval boundaries.

The API Is Making Claims

Every interface decides what survives abstraction. A memory API that returns only content and similarity scores is implicitly telling the agent that records are independent items and ranking is the only meaningful relationship among them. That was a reasonable claim when memory meant fetching passages to stuff into a prompt. It becomes a dangerous one when memory carries policy, organizational decisions, historical state, authority, and evidence for autonomous agents.

Here is what can vanish when a rich memory system is flattened into a ranked list:

Store knows API returns
B superseded A A and B
A was corrected by B A and B
A remains true but no longer governs A and B
A contradicts B A and B
A and B share the same provenance A and B
B governed the previous decision A and B
A’s authority expired A and B

From the API’s perspective, nothing is wrong. From the agent’s perspective, almost everything important is gone.

We have spent enormous effort improving retrieval: better embeddings, hybrid search, rerankers, metadata filters, graph retrieval, larger context windows. All of it helps systems find relevant information. Finding the right records and understanding what they mean in relation to one another are different problems, and agentic systems are pushing memory hard toward the second. If the store preserves that structure but the interface discards it, improving the store will not help. The API has become the lossy boundary.

A memory API that knows A was superseded by B but hands the agent [A: 0.94, B: 0.91] has not merely dropped some metadata.

It has changed the meaning of the memory.

The thread that produced this piece has already moved the problem past where I started it. “A ranked list has nowhere to put an edge” was the right first cut, and it is a statement about the response shape. The sharper version, the one I am chasing now, is that some edges need durable identities, and something has to decide which edges are worth hydrating, before ranking rather than after. That is no longer a claim about the shape of the response. It is a claim about the shape of retrieval itself. Which is a longer conversation, and, I suspect, the next one.


With thanks to Edward Izgorodin, whose post started this and whose “nowhere to put an edge” framing anchors it, and to Giulio D’Erme, Tae Kim, and Mike Czerwinski, whose thread contributions are cited above. Different directions, same wall.

Facebooktwitterredditlinkedinmail