The Data Flow Audit
A read-only audit for the bugs that never throw: a field nothing reads, a branch no caller reaches, a value on the wire the consumer ignores, a rule implemented twice and quietly drifting. It inventories the system along the path a value travels rather than by component, records what it finds, and fixes nothing — because writing the repair before you have the measurement is the mistake the audit exists to break.
slug: the-data-flow-audit · platforms: Claude Code, or any coding agent with repo access · type: run per codebase, re-run on a cadence · tier: advanced
What it is
Some bugs aren’t errors. They’re absences. A field that gets written and never read. A value sent across the wire that the other side already has and quietly ignores. A branch no caller reaches. A config key declared and forgotten. A rule implemented twice, in two places, with the copies slowly drifting out of agreement. None of these throw. All of them pass the test suite, because a test proves the thing it’s pointed at, and nobody points a test at a hole. Documentation misses them too, because docs are filed by component and these defects live in the space between components.
This is a read-only audit that hunts that whole class at once. It inventories your system along the axis a gap is actually visible on — the path a value travels — instead of the component view that hides it. It finds, records, and stops. It fixes nothing, by design, because writing the repair before you have the measurement is the exact mistake the audit exists to break.
The one mechanism that makes it work
Most audits start from a hand-written list of things to check, which means they only ever find what someone already suspected. This one builds its inventory mechanically — reflected straight out of the code, every model, schema, column, config key, and payload field — and then looks for which ends of which paths have nothing on the other side. The gap becomes a hole in a line instead of an absence a human has to notice. That inversion, from “check the list” to “enumerate everything and find the holes,” is the whole game.
Before you run it — read this part
This prompt is not paste-and-go. Three adaptations, in order of payoff:
Give it your seams. The dispatch’s boundary section assumes a frontend/backend split. Yours might be service-to-service, module-to-module, or just two people owning two ends of one rule. Replace it with your real boundaries. This is the highest-yield section in the audit, so it’s worth getting right.
Give it three or four real examples. Where the dispatch says to paste recent defects from your own codebase, do it. A field nothing reads, a dead branch that looked live, a value on the wire the consumer ignores, a test that could never fail. This single step does more than anything else in the prompt: it calibrates what to hunt for, and generic framing won’t.
Point it at real data if you have any. “Nothing writes this field” and “nothing wrote this field in the sample I could reach” are different claims. If the only data it can reach is a dev environment, every never-executed finding is provisional, and the report should say so.
Two expectations that keep it honest. A big inventory will contain confidently wrong rows, usually because a value is reached by a path the detector didn’t model — so treat the output as a map to verify, not a list to execute; if someone who knows the system says “that works,” they’re probably right. And the output is worth more re-run than read once: the rows are a snapshot, the value is the diff against the next run. A new producer-with-no-consumer means someone just added a field nothing reads. This one wants to become a script.
THE ARTIFACT
Everything below is the dispatch. Fill the one bracketed block with your real defects, adapt the boundary section to your seams, and send the whole thing to your coding agent.
Why it’s built this way
The read-only rule is load-bearing, not a safety nicety. The instant an audit is allowed to fix, it starts fixing the easy visible things and stops looking for the hard invisible ones, and you end up with a tidier codebase and the same structural holes. Separating measurement from repair is the entire discipline: you get the whole map first, then you decide what to do with it, and the deciding is a different sitting with different numbers. The “cite evidence for every never-executed claim” rule is what keeps the output from being confident fiction — reading code tells you a path can run, only data tells you it did. And the self-verification rule (“check your own method first”) exists because the single way this audit fails catastrophically is a detector that misses one common idiom in your codebase and then confidently reports a hundred false holes built on that blind spot.
Then what
The remediation plan you write against this audit’s numbers, never alongside them → pair it with The Red Team on the plan before you execute. The habit underneath “measure before you build the fix” is the same one in the essay on scoping before you start. And once you’ve run it twice and started diffing the runs, it wants to be a Save Point-style standing artifact — the rows become a tracked file, not a one-time read.
The full Data Flow Audit dispatch — 146 lines
READ-ONLY AUDIT. Inventory, not repair. Read, trace, record, stop.
No edits, no branches, no fixes. If you find something disconnected,
that is the point; record it and keep going. Take the time this
needs — an hour or more is expected.
This audit finds defects where a thing exists at one end of a path
and nothing at the other end. It is organized by path rather than by
component, because that is the axis on which a gap is visible as a
hole in a line rather than as an absence someone has to notice.
Recent examples from this codebase of the class being hunted:
[REPLACE WITH 3-4 REAL DEFECTS FROM THIS PROJECT — a field nothing
reads, a dead branch that looked live, a value on the wire the
consumer ignores, a test that could never fail]
RULES
Report what the code does, not what a comment or document says it
does. Where they disagree, record the disagreement as its own
finding.
"Never executed" is a first-class finding: a producer with no
consumer, a consumer with no producer, a branch no caller reaches,
a field nothing assigns, a payload value nothing reads.
Where real data can answer a question — a database, logs, saved
files, stored records — measure rather than reason. Cite the
evidence for every never-executed claim.
Where a fact cannot be established by reading, say so and name what
would establish it. Do not infer.
Verify your own method before trusting its output. If your detector
misses a common idiom in this codebase, every conclusion built on it
is unsafe. Say so if you find you made that mistake.
OUTPUT
Write the inventory to a machine-readable file and the narrative to
a separate file. Do not paste the table into chat. Return a short
summary only.
A. THE AXIS INVENTORY
An axis is any value that travels: an input, a stored field, a
derived value, a displayed value, an outgoing parameter.
Build the axis universe mechanically. Reflect it out of the code —
every model, schema, type, table column, config key, payload field.
A hand-written starting list only finds what someone already
suspected.
For each axis record: axis, origin, producer (file:symbol), layer,
transforms (every point that reads and rewrites it, in order),
persisted_to, consumers (file:symbol and what for), crosses_boundary,
delivered_as, ever_executed (yes/no/unknown plus evidence), and gap
(producer-no-consumer, consumer-no-producer, delivered-not-read,
dead-branch, never-executed, twin, or none).
Then enumerate specifically:
- Every stored field with zero or near-zero population in real data,
with its writer and its assigner.
- Every field an external contract or config declares that no code
path reads.
- Every branch reachable only from a caller that does not exist.
- Every value present in a payload the consumer already holds, that
the consumer does not read. This class is invisible to a reviewer
on either side, because each side is individually correct.
- Every value read but never written, and therefore always a
fallback.
B. BOUNDARIES
The worst defects live between components. Adapt this section to this
system's actual seams.
- Every rule implemented on both sides of a boundary. For each: do
they agree today, is the agreement tested, what happens when they
drift. Then find the ones nobody has noticed.
- Every place one side re-derives something the other side already
computed and sent.
- Every place one side computes something only the other side uses.
- Every value whose only home is memory — session state, cache,
registry — and what happens on restart.
C. EXTERNAL DEPENDENCIES
- Every hardcoded value derived from an external source, with
file:line, the value, when it was captured, and whether anything
compares it to that source today.
- Classify each by how it could drift: declared (the source
publishes it machine-readably), documented (prose we can read but
not diff), measured (we proved it by behaviour), assumed (neither).
Mark each honestly. An assumed value everyone treats as documented
is the dangerous case.
- Every place we hardcode a value the source already declares. Each
is a line that could be deleted.
- Where a fact cannot be settled by reading, name the test or probe
that would settle it and its cost. Do not run any.
D. THE DELIVERY END
- What the caller or user actually receives, and which delivered
values are always empty, always a literal, or always a fallback.
- Every place the system alters what it was given without saying so
— truncation, snapping, defaulting, dropping, substituting. Record
whether anything in the output says it happened.
- Every failure path and what the caller actually receives: status,
shape, and whether it names what failed. A structured error
reaching a display layer as an unreadable object is the same defect
as no error at all.
- Every silent swallow: an exception caught and discarded, a failure
returning a success-shaped value, a write whose result nothing
checks.
E. WHAT THE FLOW SHOWS THAT A COMPONENT VIEW CANNOT
- Every axis with a producer and no consumer, and every axis with a
consumer and no producer. Mechanical list, not narrative.
- Every axis implemented twice, and whether the copies agree.
- Every axis whose only record is ephemeral and therefore
unauditable after a restart.
- Every guard, invariant and automated check that exists, and what
it actually covers. State for each what it cannot see. A guard
whose universe is itself hand-maintained only catches what someone
remembered to register.
THE REPORT MUST CONTAIN
A never-executed list — every path, branch, field and loop that has
never run, with evidence. This is the highest-value output.
A drift ledger — every externally-derived constant, its
classification, when it was last verified, by what method. Structured
so a check can read it later rather than a person.
A twin ledger — every rule with two implementations, whether they
agree, whether that is tested.
A probe list — facts only a live call or experiment can settle, each
with cost and what it would establish, ordered by value. Nothing run.
What this audit could not determine, stated plainly, with what would
be needed. An inventory that quietly omits what it could not see is
worse than a shorter one that says where it stopped.
NOT IN SCOPE
Do not fix anything. Do not design a remediation plan; that gets
written against these numbers, not alongside them. Writing the
structure before the measurement is the mistake this audit exists to
avoid repeating.