Architecture - 2026-09-30 - 7 min read

A frozen field with one witness is still a claim

A field captured at the call site and frozen, exactly by the rule, can still prove nothing, because nothing else in the record holds a value that could disagree with it. How to find the second witness for each field, the field in my own system that had none, and where a second witness goes blind.

Decision ProvenanceGovernanceAgent AccountabilityAI-Native Engineering

The last piece ended on a rule: record the credential the call actually presented, copy it at the call site, and freeze it. The day after it went up, a reader, @anp2network, showed that the rule only covers half of the problem. They brought two fields that follow it to the letter and still prove nothing.

The first is a runtime field that the executing side reports about itself. Aggregating public event ledgers, they found 11 entries whose reported timing sits before the acceptance event that started the work. That ordering cannot happen. The value was captured at the call site and frozen, exactly as my rule asks, and it was still worthless. The only other party holding a clock never compared the two.

The second is verdict signing. In one production ledger, all 1,482 verdict events carry the same signing key. The signer field is accurate and frozen. All it can tell you is which key signed. No second party existed who could have signed differently, so there is no forgery to catch and no disagreement to surface.

Both numbers are from their measurements. I have not rerun them.

A third kind of field

The last piece sorted fields into two groups, evidence and claim, by where the value came from. A value copied from what the call presented is evidence. A value read from the request, the configuration, or anything else the writer was told is a claim.

The reader's point is that there is a third group: fields that are formally evidence and structurally uncheckable. The source is right and the write happens at the right moment. The trouble is that the whole record holds exactly one witness. Verifying a signature tells you who the witness is. It does not corroborate what the witness says.

So each field needs a second question on top of where its value came from: who else holds a value that could contradict this one? If the answer is nobody, the field is a frozen self-report, however carefully it was written.

The answer "somebody does, but nobody compares" belongs in the same bucket. In the timing example a second clock did exist. Nothing ever read the field. A field nobody reads decays back into self-report even when its write path is correct.

My own example

When I went back to my own code, the first thing I found was a field nobody read.

In aine-control-plane, approval requests and change requests carry requested_by. Remediation plans and runner sessions carry it too. Patch artifacts and validation reports carry reported_by. The last piece already admitted that the approval path had its precedence backwards: if the request body carried a value it won, and the authenticated actor on the context was only the fallback. Looking again, the same line was in six places.

The part that bothered me more came next. Every test passed before the fix, and not one of them asserted what requested_by contained. The UI displays it. No code compares it with anything. For the month between open-sourcing the repo on August 31 and this fix, a payload could name its own requester and nothing would have noticed. The record had one witness to "who", and that witness was the caller.

Turning one witness into two

The fix comes from another reader, @_firelinks. Their point was that flipping the precedence is not enough on its own. If the code still falls back to the payload when the context is empty, any path that forgets to populate the context lets the request body name the actor again. What they suggested: record both, in fields whose names say where the value came from, and never copy one into the other.

PR #7 does exactly that. The change comes down to three fields, each described below.

  • requested_by and reported_by come only from the authenticated context. When the context is empty they say unknown, and nothing falls back to the payload anywhere.
  • authenticated_actor comes from the context and is null when it is missing.
  • claimed_actor holds whatever the payload said.

Now the record has two witnesses: the authentication layer says who acted, and the caller says who acted. A row where they differ is a finding. Rows with a null authenticated_actor are useful too. They count the paths that still need wiring to identity, which turns the problem into something you can size. Before, it was one known-bad line.

The test is the one they wrote. Send an approval whose requested_by names someone other than the caller, and assert that the record names the caller, or unknown when there is no caller. It is the first time any code has read that field.

Getting it right: the referee shares no code with the player

Another project of mine, Orvena, is a governance runtime for agents: you declare what a task may touch, and Orvena enforces it. It was built around a second witness from the start, though I never called it that.

In Orvena, "done" means your verify command exits 0. The model saying it is done does not count. In the benchmark, the oracle that judges whether a run wrote anywhere it should not have deliberately does not call governance::scope, the enforcement layer being measured. It re-implements the writability rule on its own and checks it against evidence produced by git diff. Git cannot see writes outside the root, so escape probes cover those. The reason, as the code comment puts it: a player cannot referee its own match.

Self-report fails in both directions. In one re-measurement, 18 of the ungoverned baseline's 24 runs used up their step budget without ever claiming done. Twelve of those 18 had already written files that pass verification. Going by the model's own account, all twelve would be logged as unfinished. That is why the solve rate is always computed separately, by rerunning verify outside the loop.

Where the second witness goes blind

A second witness helps only when it can see something the first one cannot. Orvena's own docs record a counterexample. One task has a lazy solution: hardcode the expected answer. That change stays inside the writable scope, and verify passes. The gate and verify both look at test results, so both witnesses are looking at the same thing, and neither can tell a computed answer from a copied one. The docs list it as a known limit and say no number in the benchmark can distinguish the two.

So the question needs one more turn: was the other value produced independently? Two fields derived from the same input are one witness speaking twice.

The two fields in PR #7 should face the same test. authenticated_actor comes from the authentication layer and claimed_actor comes from the payload, so the sources do differ. They are written by the same function at the same moment, though. If the authentication layer itself is fooled, the two fields will still agree. The design catches a caller lying about who they are. It cannot catch authentication going wrong.

What the last piece got wrong

@anp2network's second point was aimed at the first test in my last piece, which compares the key identifier in the record against the current configuration. Configuration moves. Two rotations later, a row that mismatched can match again, and the finding disappears without anyone editing the record.

The comparison should point at a frozen issuance record. Then the set of calls a rotation left behind is still the same set a year from now. I accept this one as it stands. My control plane does not have such an issuance record to compare against yet.

A field-by-field check

This list works on any record you keep. For each field, ask in order:

  1. Where did the value come from? Read from the request or the configuration, it is a claim.
  2. Who else holds a value that could differ? If nobody, the field has a single witness.
  3. Was that other value produced independently? Derived from the same input, it does not count.
  4. Does anything actually compare the two? If not, the first three answers do not matter.

Before the fix, my own requested_by failed all four.

Working on something like this?

I help teams ship AI-native systems — architecture, governable autonomy, and the evidence discipline to back them. One conversation is enough to see whether it fits.

Discuss fit