Who Wrote This Line? Git-History Provenance for Agentic Editing

Let an AI agent edit a file you care about and a question quietly appears: which lines are still mine? Plain git blame tells you who last committed each line, but it can’t tell you whether a line still carries human intent after several rounds of machine rewriting. A small Clojure tool called us-vs-them takes a different swing at the problem: it derives per-line “caution” values purely from authorship of successive versions — no markup, no comments, no special fences — so an agent can be told to stay away from human “islands” floating in a “sea” of machine text.

This review is based on the repository’s README, source docstrings and test expectations; we did not run the tool. What follows is a reading of the design, grounded in the project’s own worked examples and pinned test values.

The Problem: Your README Has No Fence Around It

Agents rewrite whole files. That’s the point of agentic editing — but it’s also the hazard. If a human carefully rewrote the opening paragraph of a README, or carved out a hand-maintained corner of a vibecoded app, an agent that regenerates the whole file will happily steamroll it. The obvious fix — “just mark your lines” — is exactly what people refuse to do. Nobody wants to sprinkle tags into plain markdown they otherwise want to read and diff cleanly.

The README states the constraint flatly: the text must not be marked up. Plain markdown is supported as is. The two use cases it names are recognisable to anyone working with agents today: a vibecoded app where a human owns a few corners of otherwise generated code, and a generated README whose opener a human rewrote by hand. In both cases the protective signal has to come from somewhere other than the text itself.

The Idea: Authorship Is the Only Signal

The signal us-vs-them uses is authorship over time. Every version of the file was created under an identifiable author — a human or an agent. By replaying the full history oldest-first and attributing each line in each version, the tool can classify every line in the current file as human-owned, agent-owned, or somewhere in between. The source docstrings say that a single pair of versions “sees one change back”, so the tool replays the whole history instead: attribution walks the versions oldest-first, so a line’s history accumulates across the replay.

The CLI runs inside a git repository and uses that repository’s commit history as the sequence of versions (README); the history itself is what git log reports. The library itself takes any history of versions with source markers. Plain git blame (git blame documentation, general knowledge) attributes each line to its last committer, and git diff shows changes between two snapshots (general knowledge). Neither answers “how much human intent survives in this range?”

Islands and Caution Values

The output is a set of ranges — “islands” of human lines inside a “sea” of machine text. Each range carries a caution value, and the docstring is explicit about the semantics: the value is a fraction of human lines in the island.

  • 1.0 — fully human.
  • A fractional value like 0.46 — originally human, since modified by agents.
  • 0.00 — fully agent.

Fractional values are the interesting middle ground. They don’t mean “half human” in a mystical sense; they mean exactly what the docstring says — e.g. 0.75 is three human lines out of four in one island. The example CLI output in the README shows this in action: 1-3 0.00, 4 1.00, 5-7 0.00, 8-20 0.46, 21-164 0.00. Read as a map of the file, that’s a mostly-agent document with one fully human line and one thirteen-line island that humans started and agents have partially rewritten.

How It Works: The Island Rules

The heart of the design is a small set of rules for how islands behave as versions accumulate. All of them are pinned in the test expectations, which makes them unusually well-specified for a small project. Walking through each:

  1. Dilution, not splitting. An agent line landing inside a human island joins it and lowers its value, rather than splitting the island. The contamination test uses a history of three versions where an island of four lines ends up with three human lines; the asserted value is 0.75.
  2. One-way absorption. Absorption runs in one direction only. A human line landing inside an agent block does not get absorbed into the agent sea — it stays at 1.0 as its own island. The landing test expects ranges 1.0 / 0.0 / 1.0 / 0.0 / 1.0 over lines 1–6.
  3. Coalescing. Two human islands merge when the agent block between them is deleted. The rejoining test expects 0.75 / 0.0 / 0.75.
  4. The coincidence rule (“deorphan”). An isolated diff match on a line that recurs elsewhere in the document is treated as coincidence, not survival. The classic case: a blank-line-like separator that matches across a rewritten block is handed to the change like everything around it, so the human’s block stays whole at 1.0.
  5. Survival of unique lines. The flip side: a line that occurs only once and comes through unchanged is kept as human. The survivor test reports four lines as one range at 0.25.
  6. Displacement. A replacement run that displaced human lines is taken as landing in their island, even when its immediate neighbours differ. The replacement test expects one range at lines 1–4 with value 0.5 (four lines, two still human), while the agent’s untouched lines 5–6 stay at 0.0.
  7. Channel / “water”. A block of agent lines larger than the smaller piece of human island it would strand becomes its own island, and the human island parts around it — like water. The channel test expects lines 1–4 at 1.0, lines 5–7 at 0.0, and lines 8–9 at 1.0.

Together these rules encode a philosophy: be generous about crediting humans with survival, but paranoid about false positives from generic lines, and let geometry (island size versus intruder size) decide contests.

Worked Example: The Expected Values in caution_test.cljc

The test file names its histories and asserts exact per-line caution values for each. The table below is the expected values pinned in caution_test.cljc — derived from the test file itself, not from running code.

Expected values pinned in caution_test.cljc

History What it demonstrates Per-line caution as the test asserts it
base-case Baseline attribution behaviour line 1 = 0.0, line 2 = 1.0, line 3 = 0.0
contamination Dilution: agent line joins a human island and lowers its value island of 4 lines, 3 human → 0.75
landing One-way absorption: a human line in an agent block stays its own island lines 1–6: 1.0 / 0.0 / 1.0 / 0.0 / 1.0
rejoining Coalescing: deleting the agent block merges two islands 0.75 / 0.0 / 0.75
coincidence Deorphan: a recurring separator match is treated as coincidence lines 1–2: 0.0; lines 3–7: 1.0; lines 8–9: 0.0
survivor Survival: a unique unchanged line is kept as human 4 lines, one range at 0.25
replacement Displacement: a replacement run lands in the human island four-line island, two still human → lines 1–4 at 0.5; lines 5–6 (agent’s own) at 0.0
channel Water: a large agent block parts the island lines 1–4: 1.0; lines 5–7: 0.0; lines 8–9: 1.0

Two rows repay a closer look. Contamination is the rule that makes fractional values meaningful: the agent didn’t destroy the island, it diluted it — three of four lines are still human, so the whole island reads 0.75 rather than splitting into a 1.0 sliver and a 0.0 sliver. Channel is the counterweight: when the agent block is large relative to what it would strand, geometry wins, the island parts, and the agent block becomes its own 0.0 island between two 1.0 shores.

Using It: CLI and Library

Installation is via make install (see the Makefile), which requires bbin, a script installer from the babashka project. The source is Clojure (.cljc files). The README’s usage example:

us-vs-them --ours dan@eighttrigrams.net README.md

--ours lists the human authors; --theirs lists the agent authors. The rule for which to use: name the shorter list. Passing both is rejected. The example output lines are the ones quoted earlier: 1-3 0.00, 4 1.00, 5-7 0.00, 8-20 0.46, 21-164 0.00.

The library API is where agentic integration gets interesting. Two functions matter:

  • et.uvt.caution/assess takes a history (versions oldest first, each as {:text ... :source marker}) plus {:ours #{markers}}, and returns the ranges as data.
  • et.uvt.caution/degradations takes the same history plus one proposed-but-unwritten version, and returns the human lines the write would make worse — either gone, or with lower caution. An empty result means the write is safe, which makes it usable as a pre-write gate: the agent proposes its new version, you run degradations, and you refuse the write if anything comes back.

Limits and Known Gaps

The most important limitation is pinned in the test file itself, not discovered by us: a large agent block that strands a human line can leave that line unreported by degradations, even though the line has been cut off from its island. In other words, the gate can miss a specific kind of quiet damage — a human line isolated by a big agent insertion may not show up as a degradation even though its neighbourhood is gone.

There are also cross-host caveats. The test comments note that make test-cljs runs the same suite on node from a sibling “cookbook” checkout, and that hosts_test.cljc records two places where the JVM and JS hosts disagree: one number spelled differently, and an empty history that throws on one host while the other invents a line. Notably, degradations returns [] on both hosts for empty history — the gate behaviour is consistent even where attribution isn’t.

Scale and maturity are honest constraints. The repository shows 48 stars, 4 forks, 49 commits and 0 watchers, with no release or package listed. Finally, a review note claims the tool uses git log --follow --name-only to handle renames (because git log -- <path> stops at renames) — this is unverified against the current source; treat it as a lead, not a fact.

How the approaches compare

us-vs-them Plain git blame Markup-based fences
Needs markup in the text No (README constraint) No (general knowledge) Yes — human-maintained fences
Works on plain markdown as-is Yes (README) Yes (general knowledge) No — text carries tags
Fractional per-range caution values Yes (docstring: fraction of human lines in island) No (general knowledge) Depends on tooling (general knowledge)
Can gate a proposed write before it lands Yes — degradations, empty result = safe No (general knowledge) Depends on tooling (general knowledge)
Trade-off Heuristic, geometry-based; small project Per-line committer only Explicit, but humans must maintain the fences (general knowledge)

The markup column is the real trade-off: fences are explicit, while authorship-derived caution needs no upkeep but is heuristic, with the gaps described above.

FAQ

Do I have to mark up my text? No. The README states the constraint explicitly: text needs no markup, and plain markdown works as is. Authorship of successive versions is the only signal the tool uses.

Can it run as a pre-write gate for an agent? Yes — that’s what et.uvt.caution/degradations is for. Hand it the history plus the proposed-but-unwritten version; it returns the human lines the write would worsen. An empty result means the write is safe to land.

What about renamed files in git history? Rename handling is unverified here; see Limits and Known Gaps.

The Bottom Line

us-vs-them is a design worth studying even before it’s a tool worth adopting. The island rules — dilution, one-way absorption, coalescing, the coincidence rule, survival, displacement, and the channel/water geometry — are unusually well-specified, each pinned to exact expected values in the test file, which is rare for a project of this size. The degradations gate is the standout idea: a cheap, structural check an agent can run before every write, with “empty result = safe” as the contract.

The honest caveats: this is a reading of the design, not a product test — we did not run the tool — and the project is small (48 stars, 4 forks, 49 commits, no release listed), with a pinned known gap in the gate and documented cross-host disagreements in attribution. If you let agents edit text you care about, the ideas here are immediately actionable; the tool itself deserves a hands-on evaluation against your own histories before you trust its caution values in production.

How This Guide Was Built

This post is a design analysis of us-vs-them, assembled on 2026-10-23 from four primary sources fetched that day: the repository page (for stars, forks, commits, watchers and language), the README (problem statement, CLI usage, no-markup constraint), the source docstrings (assess/degradations semantics, island rules), and the test expectations (pinned per-line values, known gap, cross-host notes). This review is based on the repository’s README, source docstrings and test expectations; we did not run the tool. All numbers in the artifact table come from the test file’s own assertions; the git-rename detail is flagged as unverified; no licence claim is made because none was shown on the fetched repo page.

← Back to all posts