---
title: "I made the agent attack its own diff engine before I trusted it"
url: "https://toddpaulbrownjr.com/writing/attack-the-diff-engine/"
author: "Todd Paul Brown Jr."
description: "** I attacked Cadence's diff engine with adversarial tests before trusting it. Three bugs, three fixes, 368 tests. Why hash-based reconciliation matters."
kind: "article"
updated: "2026-09-26T02:50:25+00:00"
---

# I made the agent attack its own diff engine before I trusted it

Cadence is a desktop app I built because I'm dyslexic. It narrates Markdown files, highlights each paragraph as it's read, and runs entirely on my laptop — no account, no cloud upload. When a manuscript is revised, only the changed paragraphs re-render and notes follow the text that moved, flagged for review when the match is uncertain rather than silently reattached. The diff-and-reconcile engine that makes that work was the riskiest component in the whole build, so I attacked it before I trusted it.

## What I was building

The core is Electron with a Python/FastAPI sidecar. Documents parse into content-addressed blocks — a hash function doing four jobs at once — so highlighting, notes, change detection and audio caching all line up on the same units. When you revise a chapter, the system compares the old blocks against the new ones, identifies what changed, and queues only those paragraphs for regeneration. Notes are attached to the hash, not the position, so they follow the text even when you cut-and-paste entire sections around.

That reconciliation logic — deciding what moved, what changed, what stayed the same — was also the place where a bug could wreck everything silently. A false positive means you re-render paragraphs that didn't change. A false negative means you never update a paragraph that did change, and the audio stays stale while the JSON tells you everything is fine. Both bugs happened.

## The adversarial review

I told the agent to attack the diff engine before I built anything on top of it. The prompt was explicit: treat the reconciliation logic as hostile code, write tests that try to break it, and don't move on until it survives. Three bugs came out, each one fixed with a regression test before the next attack run.

The first bug was a hash collision on empty paragraphs. Two blank lines in different chapters hashed to the same value, so a note on one would silently attach to the other. The fix was to include the document ID and sequence position in the hash input — the same function now does four jobs: deduplication, change detection, note anchoring, and collision avoidance.

The second was a failure to detect a moved paragraph when the text was identical but the surrounding context changed. The engine compared hashes but ignored position, so it marked a paragraph as unchanged when it had actually been cut from the introduction and pasted into the conclusion. The fix added a position-delta check: if the hash matches but the index jumped more than two slots, flag it for review instead of assuming it's stable.

The third was the Windows file-attribute bug, which didn't surface in the adversarial tests because it was an OS-level quirk, not a logic error. A hidden file attribute on the entire project folder made every chapter show as stale and refuse to regenerate. The JSON writes kept succeeding, the worker couldn't log the failure, and the app looked fine while the audio never updated. Both the logging and the file-check are fixed now.

By the time the adversarial review finished, the diff engine had 368 backend tests — 305 test functions expanded by parametrization — and I trusted it enough to build the rest of the app on top of it.

## What it can do now

Cadence has narrated 2,995 segments across two real projects: a 63,000-word manuscript (1,869 segments, 6.94 hours of audio, produced in 98.2 minutes of GPU time, zero failed) and 51 company knowledge-base documents (1,126 segments, 2.51 hours of audio). The re-render logic works: when I revise a chapter, only the changed paragraphs queue for audio, and notes follow the text even when I reorganize entire sections.

The roadmap was built across three consecutive days in July. Two later single days added project management and a notes export. Status: **shipped**, to an audience of one — it has never left my machine.

## Why hash-based reconciliation matters

Content-addressed blocks let you treat a manuscript as a data structure instead of a file. The hash is the identity: if the hash matches, it's the same paragraph, even if it moved. If the hash changed, it's new content, even if it's in the same position. That separation — identity vs. position — is what makes the diff engine work, and it's also why attacking it first was the right call.

A false negative in production would mean stale audio that looks up-to-date. A false positive just costs GPU time. The adversarial review found both, fixed both, and gave me the test coverage to ship it.

You can read more about Cadence and see the full receipts on the [Cadence](/builds/cadence/) build page and the [receipts](/receipts/) table.
