← The ADLC library
Live debates · 50

Your logs are self-reported, and that is the problem

Almost every record of what an agent did was written by the agent or its harness. The actor is attesting to its own behaviour, which fails in exactly the case you built the record for. Derive evidence from artefacts the actor cannot rewrite.

Here is the question you will eventually be asked, in some form: what did this system do, and how do you know.

Most organisations answer the first half well and the second half not at all. There is a log. It says the agent was given ticket PLAT-4471, retrieved four files, produced a change, ran the tests, and opened a pull request. Timestamps, a trace ID, structured JSON. It looks like evidence.

It is not evidence. It is a statement by the actor about its own behaviour, and the class of failure you built the record for is precisely the class where such a statement is unreliable.

My position: self-reported logs are fine for debugging and close to worthless as proof. The records that survive scrutiny are the ones derived from artefacts the actor did not author and cannot rewrite.

I am not a lawyer and none of this is legal advice. This is an argument about where to put the boundary in your pipeline.

The three ways a self-report fails

This is not about a malicious agent. It is about ordinary engineering failure modes, and each one breaks the record in a different place.

Intent versus effect. The harness logs what it asked for, not what happened. It records “applied patch to billing/rates.py” because that was the instruction. Whether the patch applied cleanly, whether a retry produced a second write, whether a post-processing step reformatted half the file: none of that is in the record, because the record was written at the point of intention.

Configuration drift. The log’s contents are whatever the instrumentation captured, and the instrumentation is a code path like any other. Someone changed a log level in June. A sampling rule dropped the events on the busy path. A new tool call was added and nobody added the logging for it. Six months later the gap in the record is indistinguishable from an absence of activity.

The actor is the author. Even without bad intent, a component that both acts and describes its actions has no independent check. If the harness has a bug in how it attributes a change, that bug is present identically in the log and in the behaviour, so the record confirms the error rather than exposing it.

Notice what those three share. They all fail silently, and they all fail hardest in the situation where you most need the record to be right.

A claim about an action

  • Written by the actor, at the moment of intent
  • Contents set by instrumentation, which changes
  • Silence is ambiguous: no event, or no logging
  • Confirms the harness's own errors
  • Mutable in practice, through retention and reindexing

A record derived from an artefact

  • Written at a boundary the actor does not control
  • Contents set by what actually changed
  • Silence means nothing changed
  • Independent of the harness's beliefs
  • Append-only, with a verifiable chain
The difference is not thoroughness. A meticulous self-report and a sparse derived record fail in opposite directions, and only one of them fails in the direction you can detect.

Derive from what changed, not from what was reported

The good news is that your pipeline already produces artefacts with the right properties, and almost nobody uses them as the evidentiary spine.

The git object graph is content-addressed. A commit is what it is because of what it contains, and its parentage is part of its identity. You do not have to trust a claim that a change was made, because the change is the record. A diff cannot say it modified a file it did not modify.

The forge’s merge event is written by a system the agent is a client of, not a component of. Who approved, when, against which head commit, under which branch protection rules.

The CI record is produced by a runner executing against a specific commit, and it says what actually ran, not what was meant to run.

Build a record out of those three and you get something with a property no self-report can have: it describes effects rather than intentions, and its silences are meaningful. If the git history shows a change, the change happened, regardless of what any log said.

SpecificationWhat was asked for, recorded before the work, with an identity that later records can reference.
Change artefactThe commit. Content-addressed, so its contents are its identity. Not a claim about a change, the change itself.
AdjudicationThe merge event, written by the forge. Who approved, against which commit, under which rules.
ExecutionThe CI run, tied to a commit hash. What actually ran, produced by a system with no stake in the answer.
Four records, none of them authored by the agent, each referencing the next by an identity that cannot be quietly changed. This is the chain that answers the question six months later.

What the derived record cannot tell you

I should be equally clear about what this approach loses, because the trade is real.

Artefacts capture effect and lose intent. The commit shows the file changed. It does not show which specification the agent was working from, what context it retrieved, which tool calls it made, or why it chose one pattern over another. All of that is genuinely useful and none of it is recoverable from the artefact.

So the answer is not to discard self-reports. It is to know which kind of record you are holding, and to bind the two together. Attach the specification identity and the run identity into the artefact itself, in the commit trailer, in the merge metadata, in something that becomes part of the content-addressed object. Then the self-report becomes retrievable evidence about intent, anchored to a record about effect that the actor could not author.

Without that binding you have two systems that do not reference each other, and reconciling them six months later is an exercise nobody completes.

One structural note worth carrying into any vendor conversation: native provenance attribution exists for generated images, and there is no documented equivalent for generated code. There is no marking in the artefact that says a machine wrote this. Whatever origin record you have is one you constructed from your own pipeline’s events, which means the design of that construction is your responsibility and nobody else’s.

Where this breaks down

Git history is not immutable, it is tamper-evident, and only if you configure it that way. Force-push exists. History rewriting exists. Someone with the right permissions can change the past, and the property I am relying on only holds where protected branches, restricted force-push and signed commits are actually enforced. Anyone selling git history as an audit trail without saying that is overselling it.

Derived records are lossy in the way that matters most to an investigator. The first question after “what changed” is usually “why”, and the artefact chain is silent on why. A meticulous self-report answers that question well. My recommendation trades away the answer to the more interesting question in exchange for reliability on the more basic one, and for some investigations that is the wrong trade.

Nobody has told you self-reports are unacceptable. The strongest practical objection: auditors and regulators have not, to my knowledge, ruled that actor-authored logs are insufficient, and a well-kept structured log may satisfy every review you actually face. I am arguing from what a record can support rather than from what has been demanded, and there is a real chance I am asking you to build more than you need.

Vendors will close some of this gap. Harness and platform logging is improving quickly, and signed attestations from the tool vendor about what their agent did would materially change the picture. That would not make the actor independent, but a signed claim from a third party is meaningfully better than an unsigned claim from a component you run.

And the binding step is real work with no immediate payoff. Getting specification and run identities into commit trailers across every team and every tool is unglamorous integration work, justified by a question nobody has asked yet. That is a hard internal sell, and I would rather say so than pretend the sequencing is easy.

The takeaway

Almost every record you hold about agent behaviour was written by the agent or its harness. That is a fine source for debugging and a weak one for proof, because it captures intent rather than effect, its silences are ambiguous, and it confirms its own errors.

Build the spine of your record from artefacts the actor did not author: commits, merge events, CI runs. Bind the self-report to that spine so intent stays retrievable. Then a question asked six months from now has an answer that does not depend on trusting the thing being asked about.

If you take one thing into next week: pick a change your pipeline merged last month and try to establish, using only records not written by the tool that produced it, what changed and who accepted it. What you cannot reconstruct is the gap.