Agentic engineering operations / Field essay 39

A merge needs proof of no change.

A similarity heuristic cannot prove reviewed work is already merged; simulate the exact merge against current main and compare the resulting tree, including reverts relative to the task fork point.

Published

AGENT FLEET COORDINATION AND RELEASE EVIDENCE
FIELD ESSAY 39 / AGENTIC ENGINEERING OPERATIONS

A merge needs proof of no change

A line-coverage rule called work merged when its content was not in main.

I built a bulk reconciliation command for reviewed tasks that were still marked MERGING after their changes had reached the shared branch by another route. The first version compared added lines and treated a high similarity score as proof that a task was already covered. That was the wrong kind of evidence for a state transition that could not be undone.

The problem was not that the heuristic was poorly tuned. It was that similarity answers the wrong question. A reordered guard, a duplicated check, a mode-only change or a submodule update can leave most added lines present while the actual tree still differs. A revert can also make a commit an ancestor of main while restoring the old content. “Looks covered” is not “merging this exact work would change nothing.”

Test the operation you are about to claim happened

I changed the question from “does main resemble this patch?” to “what would Git produce if this reviewed commit were merged into the main snapshot that exists now?” The command builds a merge result without moving a ref or touching a working tree. Then it compares the resulting paths, modes and object identities against current main. If any product path would change, or the merge conflicts, the row stays unresolved.

The relevant invariant is short:

candidate is already covered only if
  merge(current_main, reviewed_commit) is clean
  and resulting tree == current_main tree outside receipt-only paths

The implementation uses Git's merge-base rather than an author-supplied diff base. For rows already in main, it also checks whether main later reverted a path to the task's fork-point version. The fork point matters: a covering commit that predates the task cannot prove that the task's later change was superseded.

That makes Git the semantic oracle for Git's own merge behavior. It accounts for more than text: path types, executable mode, submodules and conflict behavior all matter to the tree that would result. A line-based score cannot represent those semantics.

Irreversibility raises the proof bar

The state machine made the original shortcut especially risky. A row marked merged could only be cancelled afterward. That meant a false positive was not a bookkeeping typo; it would erase work from the queue's view without a supported way to restore its prior state.

So I made the command dry-run by default. Apply requires an explicit reason, and the check is repeated at the point where the state transition is emitted. Every applied row gets a receipt describing the evidence. The operator can inspect the proposed rows before committing the transition, and the durable event records why that row was considered complete.

The design distinguishes several cases instead of compressing them into a boolean: the reviewed commit is already an ancestor and has not been reverted; a simulated merge is a no-op; an operator names a later covering commit and that commit touches every remaining path; only receipt metadata remains; or the check refuses. Unknown history, missing objects, a conflict or a changed tree is not a quiet pass.

The second review found the missing history case

The first exact-tree version still had a gap. A task change could be merged and later reverted. If the classifier looked only at current tree similarity or ancestry, it could mistake that revert for successful coverage. I added a test that creates the history, reverts the change, and requires refusal. The corrected comparison starts from the task's fork point and checks the path against that version.

This is why independent review has to inspect the evidence model, not just the code's happy path. The important question was not “does the command pass its tests?” It was “what sequence of merges and reverts would make this command claim work is present when it is not?”

Where this pattern applies

Whenever an automated system records an irreversible fact—merged, paid, delivered, revoked, migrated—the proof should be tied to the operation and the exact state that makes the claim true. Similarity is useful for ranking candidates. It is weak evidence for a one-way state transition.

A practical review starts with three questions:

  1. What exact operation would make this claim true?
  2. Can I simulate that operation against the current authoritative state without side effects?
  3. What later history can make an earlier proof stale or false?

Then keep the action reversible until the evidence is checked, and repeat the check at the state transition if the underlying state can move in between.

Make the merge result the evidence

The useful object here is the candidate tree Git would produce, not a similarity score over changed lines. A tree captures paths, file modes and object identities together. That matters for a rename, executable-bit change, submodule pointer or deleted-and-recreated file, where a line diff can understate the semantic difference. The check should run against the current target snapshot and the exact reviewed candidate, because main can move after the original review.

I also separate two questions that dashboards often blur. “Is this commit already an ancestor?” answers whether its history is present. “Would merging it now change the target tree?” answers whether its content is already represented. Neither question proves that the tests ran or that the original acceptance decision still applies. A no-op merge is a narrow content claim; it must not become a general approval badge.

What a no-op proof does not say

I keep this proof narrow. It establishes that merging one recorded commit into one recorded target would not change product paths. It does not prove the feature is correct, that its acceptance criteria still match the current decision, or that the test suite ran. Those are separate gates with separate evidence. Treating the no-op result as a general green badge would recreate the same mistake at a different layer.

The test I want around this code is a small history fixture, not a large integration environment: start from a base tree, create a candidate, merge a different implementation, and then ask whether Git would change the target if the original candidate were merged. Repeat with a conflict, a mode change, a submodule pointer and a merge followed by a revert. The expected result is not a similarity percentage. It is a classification tied to the exact objects in the fixture. This makes the false-positive cases legible and keeps the decision reproducible when Git versions or repository state change.

Operationally, I also preserve the distinction between a preview and an apply. The preview is cheap and repeatable. The apply path must re-resolve the candidate and target because another writer may update main between the two calls. If the receipt names different objects from the ones the tool used, it is not evidence. A useful reconciliation tool should be able to say “I cannot prove this” without repairing the state on the operator’s behalf.

A failed proof should leave state untouched

I include the ambiguous and stale cases in the operator workflow, not only in unit fixtures. If the target branch moved after preview, the old receipt cannot authorize a merge; the tool recalculates against the new target. If the candidate cannot be resolved from the trusted review record, it stops rather than accepting a caller-supplied patch as equivalent. If the temporary merge tree cannot be produced cleanly, that is an unresolved result, not a reason to fall back to similarity. These refusals keep the tool honest at exactly the points where it would otherwise be tempted to guess.

For an operator, the safe sequence is: resolve the candidate commit from the review record; calculate the merge result without moving a ref; reject conflicts; compare the result with the target tree; record the exact target and candidate identities; then require the separate test and approval evidence. If the target changes, recalculate. If the tool cannot produce an exact result, keep the state unresolved. Which state transition in your release process still relies on a vague “looks merged” signal?

How this was verified

I checked the command and its Git-backed tests at the pinned runner commits, including the revert-after-merge case and the dry-run/apply boundary. The article describes the implemented reconciliation logic; it does not claim that similarity scoring is never useful for other tasks.

CONNECTED READINGForward and back
Keep reading

The answer deserves
another experiment.

Back to the journal