close

DEV Community

Cover image for Designing a Reasoning Ledger Record
Ken W Alger
Ken W Alger

Posted on Originally published at kenwalger.com

Designing a Reasoning Ledger Record

Built from community debate on agent audit trails

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (14)

Collapse
 
p0rt profile image
Sergei Parfenov

obtained is the right cut. three identical version: 7 fields with three different trust levels is the exact failure i wrote up as the provenance vector dying at the storage boundary, and ur schema is the first i've seen that keeps it alive past the write.

one push: inherited flattens a chain into an enum. a version inherited from session state has whatever trust the record it came from had, and that record may itself be inherited. so obtained: inherited needs inherited_from: decision_id, and effective trust is the weakest link along that chain, not the label on the last hop (mike czerwinski called it the lattice meet in my july thread, credited). without the pointer, two hops look identical to one, and the ledger can't say how far the evidence sits from anything actually fetched.

and on stakes-dependent fields: who decides a decision is low-stakes enough to skip alternatives_considered, and is that classification itself a ledger event? if the agent grades its own stakes, the low-stakes path is where the unrecorded decisions go.

Collapse
 
kenwalger profile image
Ken W Alger

I think both pushes are right.

On inherited, agreed that the enum describes the acquisition method but not the provenance chain. If I inherit v7 from session state, the important question immediately becomes “where did the session get v7?” If that points to another inherited record, the chain has to remain traversable until we reach something that was actually fetched/witnessed or until the provenance runs out.

I like the weakest-link framing there too. Inheritance can't manufacture stronger provenance than its source possessed. A ten-hop chain ultimately rooted in a witnessed authority fetch is a very different object from a ten-hop chain whose third hop says, effectively, “the agent reported this.” Without inherited_from or an equivalent pointer, both collapse into method: inherited and we've thrown away exactly the information obtained was intended to preserve.

The stakes question is the nastier one. I don't think the agent making the decision should also have unilateral authority to classify that decision as low-stakes and thereby exempt itself from recording the evidence we'd need to examine that classification later. That's structurally circular.

I'd put stakes classification on the governing side of the boundary: policy/runtime/tool semantics determine the minimum evidence requirements for the class of action being attempted. The agent can report its assessment of stakes, but that shouldn't be what grants the reduced-record path.

And yes, for consequential distinctions I'd want the classification itself to be observable. Otherwise six months later an abbreviated record can't tell us whether it was legitimately classified as low-stakes or simply skipped the fields that would have made the decision examinable.

There's an interesting symmetry between your two points: inheritance shouldn't be allowed to manufacture trust, and classification shouldn't be allowed to manufacture exemption. In both cases the record needs a path back to an authority outside the claim it's being asked to trust.

Collapse
 
suraj09 profile image
Suraj Suradkar

The “witnesses, it does not enforce” boundary is the part I’d defend hardest too. Once the ledger becomes the thing deciding whether an action can proceed, you’ve coupled the evidence of a decision with the mechanism enforcing it — which makes the audit trail much harder to trust independently.

I also really like the “supersession is a new event, never a rewrite” rule. For agentic systems, being able to answer “what did the system know and why did this decision make sense at that point in time?” is much more valuable than simply knowing the current decision.

The two-clock relationship model is an interesting extension here. I’m curious how you’d handle a decision whose underlying evidence changes without anyone explicitly making a new decision — would the revalidation event simply mark the old decision as potentially stale, or would you create a new ledger event that changes its validity?

Collapse
 
kenwalger profile image
Ken W Alger

I think @pm25coder has covered the mechanics here well, and the part I'd underline is that the ledger shouldn't become a heartbeat log of every successful check. "We checked again and nothing changed" is operational telemetry; "the authority changed state" is historical evidence.

That distinction also helps preserve the ledger's purpose. I want it to answer questions about decisions and meaningful changes in the evidence surrounding them, not become an exhaustive trace of everything the system did while reaching those decisions.

Where I'd add one wrinkle is the negative-space problem we've been discussing elsewhere: if policy requires a freshness evaluation, a decision not to revalidate may itself need witnessing. Otherwise "revalidation wasn't required" and "the revalidation mechanism never ran" become indistinguishable. So I'd preserve state transitions and explicit policy decisions, while leaving routine unchanged observations to operational telemetry.

Collapse
 
pm25coder profile image
pm25coder

The two options you name are one mechanism seen from two sides, and the key is that neither writes on the old record. Revalidation runs outside the ledger (Principle 7). When it re-fetches a referenced authority and finds the evidence moved, it emits a fresh observation, and that observation becomes a new ledger event pointing at the old decision - carrying a 'potentially stale' marker. The old record is untouched; what changes is how it reads, because validity is derived at query time by walking the chain: the decision event plus the latest revalidation events on each authority it cited. That split keeps two questions apart: 'was this decision reasonable given what was known then?' stays answerable from the original record (Principle 2 - never rewrite), while 'is it still current?' comes from the newest revalidation event. Since that event carries its own asserted_at (Principle 4), replaying March filters it out and sees March the way March saw it. One distinction I would add: 'potentially stale' (an authority was observed to move past our reference) is not the same verdict as 'unreachable' (we can no longer check) - they imply different actions, review vs re-fetch. And the honest limit: if the change never crossed the boundary, no revalidation sees it. The marker exists only for observed changes.

Collapse
 
suraj09 profile image
Suraj Suradkar

That distinction makes sense. I especially like the separation between “potentially stale” and “unreachable” — they imply very different operational responses.

The query-time validity model is also interesting because it preserves the historical decision without pretending that historical validity and current validity are the same thing.

One thing I’m still curious about: how do you prevent repeated revalidation events from becoming noisy for frequently changing authorities? At some point, the system needs to distinguish meaningful state changes from routine observations.

Thread Thread
 
pm25coder profile image
pm25coder

Good question - the noise budget is set by three properties of the design.

First, revalidation is query-time, not a background poll. The ledger doesn't run a scheduler that re-checks authorities on a timer; it re-fetches a referenced authority only when that reference is about to be consulted in a decision. Frequency is bounded by real use, so a frequently-changing authority only produces events as fast as it's actually used.

Second, the record changes on a verdict transition, not on a check. The revalidation event classifies the authority as current / stale / unreachable. A routine check that finds the same fingerprint (version, hash, etag) as the last one is a "current" observation: it moves the last-verified timestamp but does not append a new marker. Only a verdict change writes a new record, so routine observations stay silent.

Third - the part that dissolves the question for me - the ledger doesn't decide what's "meaningful". It records observations; meaningfulness is a read-time judgment. A reader facing a volatile authority sees the verdict plus last-verified time and applies its own threshold: "re-verified ten minutes ago and still current" is enough for a low-stakes call, and a high-stakes call re-checks on the spot anyway. The ledger's job is to keep "verified current", "potentially stale", and "unreachable since D" distinguishable - not to editorialize about which changes matter.

Collapse
 
alexshev profile image
Alex Shev

The post makes a useful distinction between a feature working once and a system being dependable. I’d add an explicit failure-mode checklist so the next contributor can see which assumptions are intentional and which ones still need evidence.

Collapse
 
kenwalger profile image
Ken W Alger

I like that, particularly if the checklist describes the limits of the record rather than becoming another claim that the ledger can prove more than it actually can.

Something like known_failure_modes, unverified_assumptions, or coverage_limitations could be useful for consequential decisions, especially when the absence of evidence might otherwise be mistaken for evidence of absence.

The principle I'd want to preserve is the one behind the "Honest Limit" section: the record should make it easier to see what wasn't established, not merely provide increasingly detailed evidence for what was.

Collapse
 
alexshev profile image
Alex Shev

Yes. I’d make those limits first-class fields rather than prose buried at the end. A ledger is most useful when it lets the next reader see both the evidence and the edge of the evidence without having to infer either.

Collapse
 
edmundsparrow profile image
Ekong Ikpe

I actually think the audit trail is a cool idea. I’m just wondering whether it belongs in the agent’s continuity layer. My approach is to put durable design intent in the help/domain layer, where the agent can use it alongside the code to understand and maintain the system.
The audit trail is useful, but it creates another thing to maintain, retrieve, and carry forward. Can we throw that weight out of agent memory? It doesn’t have to disappear — it can live elsewhere when provenance matters.
Medicine doesn’t need to carry the entire history of archaeology to remain useful. Different kinds of knowledge can have different homes. Happy weekend 😉

Collapse
 
kenwalger profile image
Ken W Alger

I think your instinct is right that these kinds of knowledge should have different homes. I don't want the agent carrying its entire decision history around any more than I want every database query dragging the transaction log into memory.

Where I'd draw the distinction is between custody and availability. The ledger can absolutely live outside the agent's continuity/memory layer. In fact, I think it should, because it has different integrity and retention requirements. But when the agent retrieves an artifact governed by a previous decision, the relevant piece of that history should be available through the same context interface.

That's the "separate custody, one interface" idea pm25coder mentioned below. The archive stays an archive. The agent gets the relevant chart, not the entire archaeological dig.

So yes, throw the weight out of working memory. I just wouldn't throw away the relationship that lets the right piece of provenance come back when it matters.

Collapse
 
pm25coder profile image
pm25coder

Your instinct matches where the piece lands — Principle 7 is basically this: separate custody (the ledger lives outside agent memory) and one interface (retrieval reunites the artifact and its decision history when the relationship becomes relevant). What makes it cheap in practice, running one: it's write-only at decision time — an append with references, no re-read, no scanning — and the agent's working memory only ever holds the single record that rides along when the governed artifact gets touched.

So the sharper frame than medicine-vs-archaeology is chart vs archive: the chart (decision context for the artifact in hand) rides along; the archaeology (full history) stays in the ledger store. The failure mode isn't storage weight — it's a ledger that's separate AND never surfaced. Then provenance "matters" only to someone who has to know to go looking — and nobody goes looking mid-task.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.