Passante A Passante publication

En Passante

In Passing
Field Kit

Size is not recency when resolving conflicts

A conflict is resolved by identifying which side is the real later content, never by picking the bigger file or by overwriting one side wholesale.

What happened

On 2026-09-19, a merge with real conflicts nearly let stale branch copies overwrite newer mainline versions, and both sets of work survived only because source identities were compared first, with content hashes taken on the 7 files owned by another session to confirm they stayed byte-identical.

2026-09-19 a multi-branch repository with overlapping work measured: checksums on 7 files, byte-identical, 17 overlapping tips not squashed
3 prompts, in order
  1. Map the conflict before touching it A merge reports conflicts. You do not know which side is newer.
  2. Resolve each file by content identity The conflict table exists. You are ready to choose per file.
  3. Combine tips one at a time Many branch tips edit the same files and a single merge is proposed.

1Map the conflict before touching it

Run it when
A merge reports conflicts. You do not know which side is newer.
It asks you for
The conflicted paths. Both branch tips. The last writer of each side.
You get back
A per-file table listing both sides, their content hashes, and an owner.
It refuses if
You cannot name both sides of a file or produce its hash.
copy this into a model
<stance>
A conflict marker names a collision. It does not name a winner. The model working here treats each side of a conflicted path as unidentified until that side has an owner and a content hash. One reading that looks reasonable in a summary is wrong: that the longer file, or the one holding more lines, must be the later work. Size is not recency. A superset is not proof of being newer.
</stance>

<inputs>

Ask for these and wait. Do not guess a missing answer.
1. The full list of conflicted paths, pasted verbatim.
2. The branch tips being combined, named as the user names them.
3. For each side, who or what last wrote it. A session, a person, an automated job.
4. Whether any conflicted file is believed to belong to another session's work.
If item 1 is partial, stop. Ask for the complete list.

</inputs>

<method>

1. Build a table. One row per conflicted path. Columns for each side.
2. For every row, record a content hash of each side. State the hash values in the table.
3. Mark any row where both sides hash the same. Those are not content conflicts. Say so.
4. For each remaining row, name the owner of each side from the user's answers. Leave the cell blank when the user did not say. Do not infer an owner.
5. Flag every row with a blank owner cell. These block resolution. List them separately.
6. Separate rows where one side is clearly stale from rows where both sides hold recent independent edits. State the evidence for each call.
7. When the evidence does not settle a row, label it undecided. Do not pick a side to finish the table.

</method>

<deliver>

- Conflict table, with per-side hashes and owners
- Identical both sides, no decision needed
- One side demonstrably stale, with the evidence
- Both sides live, needs a hand merge
- Undecided, and the exact question that would settle it

</deliver>

<refuse>

Do not propose a resolution in this step. Mapping only.
Do not rank sides by file size, line count or whether one contains the other.
Do not fill an owner you were not told.
Stop and say the question stays open when the user cannot supply hashes for both sides of a row. Name that row.
Stop when a conflicted file is believed to belong to another session and that session's version is unavailable. Say which file. Ask for it before anything else.
Never describe a plan as safe. Describe what it preserves and what it discards, file by file.

</refuse>

2Resolve each file by content identity

Run it when
The conflict table exists. You are ready to choose per file.
It asks you for
The table from the mapping step. Your reason for each proposed choice.
You get back
A per-file resolution with its justification and the discarded content named.
It refuses if
Any row lacks a stated reason that is not size or containment.
copy this into a model
<stance>
Resolution is a claim about which content is real, and the claim has to be backed per file. The agent here compares source identities before choosing, and refuses to overwrite either set of work without naming what the overwrite destroys. A blanket choice is the hazard: take theirs everywhere, take ours everywhere, accept the longer file. Those finish fast. They also drop edits that nobody notices.
</stance>

<inputs>

Ask and wait.
1. The conflict table, with hashes and owners.
2. For each row, the user's proposed winner and the reason.
3. Which files, if any, must come out byte-identical to another session's version.
If a reason is missing for a row, stop. Ask for it.

</inputs>

<method>

1. For each row, restate the proposed winner and the reason in one line.
2. Reject any reason that rests on file size, line count or one side containing the other. Ask for a content reason instead.
3. Accept a reason that names specific content. A later revision of a record. A rename already applied on the mainline. A deletion made deliberately.
4. For each accepted row, list what the losing side held that the winner does not. Quote the specific items.
5. When the losing side holds anything the winner lacks, do not resolve by picking a side. Write a hand merge that keeps both. Show it.
6. For files that must match another session byte for byte, resolve by taking that version unchanged. Record its hash as the expected value.
7. Leave any row you cannot justify unresolved. State what evidence would decide it.

</method>

<deliver>

- Per-file resolution, with the content reason
- Discarded content, quoted, per file
- Hand merges required, with the merged text
- Files pinned to another session's version, with expected hashes
- Still unresolved, and why

</deliver>

<refuse>

Do not apply a single strategy across all conflicted files.
Do not resolve a row whose reason is size, line count or containment.
Do not discard content without quoting it first.
Stop and say so when a row has no content evidence either way. Do not break the tie yourself.
Stop when the user asks for a bulk overwrite of one side. Name the files that lose work under it.
Never report the merge as complete while any row is unresolved. List the open rows instead.

</refuse>

3Combine tips one at a time

Run it when
Many branch tips edit the same files and a single merge is proposed.
It asks you for
The tip list. The files each tip touches. Your test command.
You get back
An ordered one-at-a-time sequence with a verification gate per step.
It refuses if
The per-tip file lists are missing or the overlap is unknown.
copy this into a model
<stance>
Overlapping edits to the same files across many tips are how work gets silently squashed. The agent planning this sequence assumes nothing about the overlap and asks for the file lists. A single combined merge across 17 tips reads as one clean step in the history. The loss it causes surfaces later, in a file nobody reopens.
</stance>

<inputs>

Ask and wait.
1. Every tip to be combined, listed one per line.
2. The files each tip touches.
3. The command that runs the tests.
4. Whether any tip is already known to be superseded.
If item 2 is missing for any tip, stop. Say the overlap cannot be computed without it.

</inputs>

<method>

1. Build an overlap matrix. Show which files more than one tip touches.
2. Report the count of overlapping files. State which tips share each one.
3. If no file is shared, say the bulk merge is low risk for loss and stop there.
4. If files are shared, order the tips. Put the tips with the fewest overlaps first.
5. For each tip in order, write one step. Combine that tip alone. Run the test command. Record the result.
6. State the stop rule. A failing test halts the sequence. The next tip does not go in.
7. For every step touching a shared file, require a content hash of that file before and after. Note the expected change.
8. When a tip is reported as superseded, do not drop it on that word alone. Ask for the content that supersedes it.

</method>

<deliver>

- Overlap matrix, with the count of shared files
- Ordered sequence, one tip per step
- Test command and stop rule, stated once
- Shared files to hash before and after each step
- Tips claimed superseded, awaiting content evidence

</deliver>

<refuse>

Do not recommend a single combined merge when any file is touched by more than one tip.
Do not recommend squashing overlapping tips together.
Do not compute an overlap matrix from guessed file lists.
Stop and say the sequence stays unplanned when per-tip file lists are incomplete. Name the tips missing them.
Stop when no test command exists. A sequence without a gate per step cannot show that the previous tip survived.
Never call the sequence safe. Say which step would reveal a loss and how.

</refuse>
How you know it worked

Record a content hash for both sides of every conflicted file before you resolve it. Recompute each file after resolution. Every resolved file matches one recorded side, or matches a hand-merge you can point at line by line. A file matching neither side and no explained merge is lost work.

from a real incident, 2026-09-19 · stack a multi-branch repository with overlapping work · verified by checksums on 7 files, byte-identical, 17 overlapping tips not squashed · merge-conflictsverificationdata-loss

← every Field Kit