---
name: app-you-didnt-write-checks
description: 21 rules from the Noesa course "The app you didn't write". For someone shipping real software with AI doing most of the typing — who has been surprised by their own codebase, and may not know what language it is in.
---

# The app you didn't write — the rules

Use with: Claude Code or Claude (save as a skill), Cursor (save under .cursor/rules as .mdc), ChatGPT or any other assistant (paste the text below into custom instructions or a project's instructions).

21 rules, taken from the course at https://noesa.leafsoft.online/c/app-you-didnt-write

Each heading is one thing the course teaches. Most are checks to run on your own output before presenting it as done; a few are background you are expected to have. 21 also name a mistake models make by default, under "Watch for". "Wrong by default" lists 62 specific ones.

Apply these to the thing you are producing — the type, the schema, the query, the copy — not only to how you explain it. Where a rule names a field, a format or an identifier, that name belongs in the output.

## Find out what you are actually holding

Say what your project is built from, where it starts, and which files are really yours.

**Watch for:** If you asked an AI how large your codebase is, or where a particular feature lives, what would it likely get wrong?

It answers from what is on disk, and it cannot tell a file you wrote from a file your build wrote — to a reader, both are just files. Generated copies are usually the *newest* things in the folder, so they look the most current and get quoted first. You will get a confident number that is several times too big, and sometimes a file path inside a folder that gets overwritten on every build. Ask for the answer from the list your history tool keeps, and make it name which folder it looked in.

**Wrong by default:**
- A line count taken over a whole folder counts generated copies as if a person wrote them. Count what the history tracks, then narrow to the folder the app ships from — here that is 42 files and 44,510 lines, roughly a quarter of the first number.
- The file a browser-based app starts at is the page, not a code file. Ask which file the machine opens first, rather than which file reads like a beginning.
- Never accept a tool named in an answer unless it appears in the project. There is no bundler here at all, which is exactly why the load order written in the page is the order the program runs in.

## A comment is not a mechanism

Find a place where one fact is worked out twice, and delete one of them.

**Watch for:** If you asked an AI to add one item to a list that a function handed back, what would it likely get wrong?

It will read the note above the function and treat it as the contract, because in most codebases the note is the only description of the contract that exists. It has no way to run the code, and no reason to suspect the one sentence written specifically to reassure the next reader. So it writes a correct-looking change against a promise nobody is keeping — and then, being tidy, copies the promise into your new code, where it will outlive the version of the function that made it true. Verify a guarantee where the value is produced, and prefer a value the machine refuses to change over a sentence asking politely.

**Wrong by default:**
- Check a claimed guarantee in the code that produces the value, never in the note above it or in the code that consumes it. Here one branch returns the original list untouched, so every caller was editing the shared plan.
- Copying a claim spreads it instead of checking it, and the copy will outlive the version that was true. Make the value itself refuse the change, so a wrong assumption stops the program rather than the reader.

## Every number needs a ruler

Name what a number is measured against, and refuse one that has no answer.

**Watch for:** If you asked an AI to size something on screen, or to line two drawn things up with each other, what would it likely get wrong?

It will pick a reference that is available rather than one that is correct, because the available one is right there in the same piece of data and the correct one may not be written down anywhere. Both choices produce clean, working, plausible code, and both look right on the screen size you happen to test on. The result is a mistake that scales: invisible on one shape of screen and badly wrong on another. Ask it to state, in words, what each number is measured against — and treat a number whose ruler cannot be named as not yet a measurement.

## Your check is blind in the same direction as your bug

Say what a check cannot see, before you decide what its green result proves.

**Watch for:** If you asked an AI whether a piece of code is well tested, what would it likely get wrong?

It counts what exists, because that is what is readable. A pass count, a file list and a note about exclusions are all written down. What a check *cannot reach* is written down nowhere, so it has to be worked out. It will also quote the note above a loader rather than the loading code, since the note is addressed to a reader and the code is not. And it reads an ordering claim as a claim about the value, because almost no project records which kind each check is. Ask it to name, in files and lines, what the checks never load — and for each check, whether it pins a number or only a ranking.

**Wrong by default:**
- A check's reach is the list of files it can load, not the size of its pass count — so measure the reach in files and lines before reading anything into a pass. Here fifteen of the source files are never loaded, which is 19,952 of 44,477 lines, so a full green run is silent about everything a player watches move.
- Read an exclusion list out of the code that does the loading, never out of the note above it, because a note about another file's state rots without a sound. The note here names five files and the real list leaves out fifteen, understating the gap threefold.
- Ask of every check whether it pins a value or only an ordering, and record which — an ordering stays true while every value is wrong together. One hundred and eighty checks asserting that a wild swing is riskier than a block all passed while an innings scored 9.69 runs an over against a real 3.10.
- Treat a fallback answer as a silence generator and check coverage of the lookup itself, table by table, rather than the behaviour around it. Four tables here quietly handed a new bowler type the wrong default, which threw nothing and failed no check.

## Verify one layer closer to the user

Tell "the file was found" from "the thing draws", and check the second one.

**Watch for:** If you asked an AI to add an image or an icon to an app and make sure it ships, what would it likely get wrong?

It verifies at the layer it can read. The file is on disk, the reference is spelled right, the packaging rule looks correct — three real checks, all above the one that matters. It cannot open the finished package, load the page, or hold the phone, so the last rung of the ladder is invisible to it rather than skipped. It will also reach for the shortest packaging rule that works today, and an adding step is shorter than one that makes its destination match its inputs. Ask for the evidence one layer down: a listing of what is inside the built thing, and what the page said while it loaded.

**Wrong by default:**
- List the contents of the thing you shipped and compare it against what the code asks for, because a green build reports on the steps it ran and not on what ended up inside. Four badges here built cleanly and were absent from the installed package, since the copying step took loose files and never looked inside a folder.
- Treat a missing picture as silent by default and put a real check one layer closer to the screen, because nothing upstream is watching. A page that cannot find an image tells the build nothing, fails no test, and draws a gap that only a person or a script looking at the result can see.
- Prefer a step that makes its destination equal to its inputs over one that adds to whatever is already there, so that producing nothing empties the folder instead of preserving a stale copy. An adding step whose input list went empty left the previous week's pictures in place, and the package kept looking full for seven days.
- Verify anything involving text size, screen edges, focus on launch or a real outside service on a real device, because a desktop browser and a pretend phone both decline to reproduce these. One setting on the phone grew every label without growing its box, and the same build looked correct in every emulator.

## Structure you can read, behaviour you meant

Name the cases where a condition is true and the behaviour you meant does not follow.

**Watch for:** If you asked an AI to hide a row of controls, or to group buttons into rows, what would it likely get wrong?

It reaches for the instruction that reads as the behaviour. Marking something hidden reads like a property of the thing, not like one competing rule among several, and grouping by a shared parent reads like grouping by what you see. Both are the common pattern, both are short, and both are correct in most projects — which is exactly why they are the default. What the AI cannot do is hold your whole set of styling rules at once and rank them, or look at the screen and count the rows an eye sees. So ask it for the list of every rule that decides the same thing, and verify the result where the result is decided: on the drawn screen, in each layout it can be in.

**Wrong by default:**
- A check that reads back the instruction you gave proves only that you gave it, so verify the end state where the end state is decided. The attribute really was set on three rows here, and one layout rule on those same rows outranked it, so all three stayed on screen as live choices for as long as the feature had existed.
- A control that refuses a press is not the same as a control that is gone, because a person still reads it as an offer and gets no answer back. Three setting rows sat filled in and tappable while the code behind them silently declined every change.
- Before trusting a weak instruction, list every rule that decides how the same thing is laid out and rank them, because the strongest one decides. The rows in question sat in a group whose own rule laid them out as a grid, which beats the browser's handling of the attribute outright.
- Check a hiding rule in every layout a screen can be in, because the rule that outranks it may exist in only one of them. A sideways-only block kept a four-button strip live over the playing area where it could swallow a shot, while upright the same strip disappeared correctly.

## What are all the ways this ends?

List every way a stored choice stops applying, including the times it is abandoned.

**Watch for:** If you asked an AI to clear a stored value once the thing it belongs to is over, what would it likely get wrong?

It clears on the ending the code already talks about. The successful path is written down, named and quick to find, so it becomes the whole answer to "when is this over". Failures and refusals are endings too, often in other files under other names, and abandonment usually has no code at all. The shortest change that works clears on the happy path and passes a check written the same afternoon. Give it the list of endings, and demand the two that reach no clearing: the failure, and the person who closed the tab.

**Wrong by default:**
- Enumerate every edge on which something's scope closes before writing the clearing, then put the clearing where the scope actually closes rather than on the ending you pictured. A played shot is one of five ways a ball ends here, and bowled, missed, left and abandoned each carried a direction into the next delivery that nobody had chosen.
- A value set deliberately before an event must survive until that event, so never move a clearing earlier than the thing it belongs to. Clearing this one at the moment of setting would have turned a held aim back into a flick and thrown away the direction a player chooses during the run-up.
- Treat abandonment as a real ending with its own exit, because the path that abandons work never passes through the place where finished work is tidied away. One flag left set by an interrupted delivery answered yes for the rest of the innings, and the batsman at the other end stopped facing, with no error and no stuck screen.
- Shape a check around the whole set of endings rather than the one you implemented, or it will agree with you on exactly the case you already handled. A check asserting the value is cleared after a played shot passes while four other endings leave it set.

## Silence is not success

Make an empty result announce itself, instead of letting it pass for a real answer.

**Watch for:** If you asked an AI to add a new option to a system that keeps its settings in several separate lists, what would it likely get wrong?

It will find the lists it can see from where it is reading and fill those in. The ones it misses are the ones kept in a different file for a different job — and those are exactly the lists nobody remembers either, which is why they were forgotten in the first place. Nothing then fails, because a well-built system usually supplies a quiet default for a missing entry, and a quiet default is indistinguishable from a deliberate one. You get a new option that exists, is selectable, and behaves like the oldest option in the set. Ask which lists the new value must appear in, get the answer as a list of file names, and check each one by hand.

**Wrong by default:**
- A lookup that answers 0 for an entry it does not hold cannot be read as a measurement, because the reading and the absence are the same value. Two separate lists here both answered 0 for a shot neither contained, and an off-side shot scored at a median of 6 degrees against the cover drive's 44 on the other side.
- Agreement between two sources that share the same default is not corroboration, it is one absence counted twice. Make every entry differ from every other entry in the thing being looked up, so a gap collides with a real value instead of impersonating one.
- A silent default produces no failure to investigate, so a passing set of checks says nothing about a list that nothing checks. Assert the whole path from the stored value to the observed result, not that the entry exists: the first guard written here checked only for presence, and presence was never the problem.

## One identifier, derive the rest

Make a mismatched pair impossible to save, rather than something you find and correct later.

**Watch for:** If you asked an AI to fix a report or a screen that is showing the right numbers against the wrong name, what would it likely get wrong?

It will find the mismatch, explain it correctly, and repair the one call you showed it. That is the shortest change that makes your example right, and it is what it has seen done a thousand times in the code it learned from. What it cannot see is the other places that combine the same two things, or which of those are deliberate — that knowledge lives in somebody's head, or in a note nobody wrote. So the trap survives, now with a comment above it saying it was fixed. Ask for the repair as a change to what the code will accept, so the wrong pair stops being possible, and ask for the list of every other place that combines the same two values.

**Wrong by default:**
- Take one identifier and derive everything else from it, rather than correcting the second of two arguments that must agree. A corrected argument leaves the wrong pair writable at every other call site, and a signature that takes both cannot tell a right pair from a wrong one.
- Reading a value after the state moves on is sometimes the point, so audit each site and record which readings are deliberate. Of four such reads in this routine, three were correct by design — one follows the bat, one loads the next man to face, one names the incoming batsman — and one was the fault.
- When a file cannot be loaded by the test setup, guard the SHAPE by reading the source text: assert the call is never given two arguments, and that every call names the value captured before the state moved. The drawing file here needs a canvas, so the card can have no ordinary test — and both shape guards were proved by putting the bug back.

## A finding from an impossible input

Demand a real, concrete path to a number before you act on it.

**Watch for:** If you asked an AI to find the biggest problem in a report, a dashboard or a set of measurements, what would it likely get wrong?

It will find the most extreme cell and explain it fluently, because an outlier is what "biggest problem" looks like in almost every set of numbers it has ever seen. What it cannot know is which rows describe something that can actually happen. That knowledge is not in the table; it lives in the rules of the thing being measured, and those are usually written down nowhere. So the most extreme cell is very often the most impossible one, and you get a confident, reproducible finding about a situation that has never occurred. Ask for the real event behind the number — one concrete case, and how often it happens.

**Wrong by default:**
- A measured cell becomes a finding only once you can name the real event that produces it and say how often it happens. The bodyline plan never bowls a good length outside off, so its fielders are set elsewhere; on the ball it does bowl it conceded four 56.7% of the time against 85.6, 91.8 and 95.8% for the other three, and was the only one of the four to take a wicket.
- Compare rows only across inputs that every row genuinely receives, because a shared impossibility flatters whichever rows happen to ignore it. The same three plans that looked fine on that ball look terrible on a short one at middle, for exactly the same reason: none of them bowls that either.
- When a tool can print a number for a combination that cannot occur, change the tool before the thing it measured: make the impossible cell print n/a and put the hypothetical behind an explicit switch. A tool that makes a mistake easy will make it again — this one had been written hours earlier to stop the first version of the same error, and permitted the second.

## Show me the measurement before you change anything

Demand the number behind a request as its own separate, checkable step.

**Watch for:** If you asked an AI to make an image, a column or a panel ten per cent narrower, what would it likely get wrong?

It will measure whatever is easiest to reach, and what is easiest to reach is the container. A file, a box, a wrapper and a cell all report their own size readily; the thing drawn inside one of them usually does not. So it takes a real measurement, does correct arithmetic on it, and hands back a change that is right about the container and wrong about the subject by however much empty space sits between them. Nothing in the answer looks uncertain, because nothing about it was. Ask for the measurement as its own step, and ask what it was taken off, before any change is made against it.

**Wrong by default:**
- A measurement taken from a container measures the container. Ask for the first and last position that actually holds the drawing: here it runs from 204 to 309, so the figure is 105 wide and the file is nearly five times wider than the thing inside it.
- A percentage is only as trustworthy as the number it is a percentage of. Fifty-one is a tenth of the empty container and half of the drawing inside it, so correct arithmetic on the wrong base removes 49% of the subject.
- Never read a subject's proportions off the shape of the thing holding it. Measured, this figure is 105 wide and 288 tall, nearly three times taller than wide, inside a container that is perfectly square.

## Validate the instrument against the whole set

Refuse a number whose measuring tool has never itself been checked.

**Watch for:** If you asked an AI to write a check that flags the bad rows in a data set, what would it likely get wrong?

It writes the check against the cases it can see in front of it. What it cannot see is the shape of everything your set really holds — the refund, the row where a field is legitimately empty. So the check is sound on the examples it was shown and blind to the kinds it was never given, and a kind it has never met produces no failure at all. It also cannot know whether a limit you handed it came from your data, so it uses the number as given. Run the check across records you know are correct, and treat every known-good row it flags as a fault in the check.

**Wrong by default:**
- A range described as a set's outer limits is a claim, and one command settles it: run the range over the set. Measured across all 60 figures this game loads, 26 of them — 43% — fall outside this one, and the real spread is 9,462 to 18,755, more than twice as wide.
- Never alter the work to satisfy a limit that has not been derived from the thing it judges. Here the reference pose every other figure is drawn against clears the lower wall by 3, so the limit is failing the very set it claimed to describe.
- Time in service is not evidence, and a number copied into a checker carries the authority of a measurement without being one. Ask which set it was derived from and on what date; when nobody knows, re-derive it before changing anything to satisfy it.

## A checker in the maker's hands becomes a target

Say why handing your check to the thing being checked destroys the check.

**Watch for:** If you asked an AI to write the tests for code it had just written itself, what would it likely get wrong?

It writes checks that the code it just produced passes, because that code is the example in front of it and the shortest way to a green result is to describe what already happens. The tests then record the behaviour it built rather than the behaviour you wanted, and the two are only the same if it got everything right the first time. Worse, both are now wrong in the same direction, so the suite goes green and stays green over the fault. Have the checks written from the requirement before the work exists, or by a pass that never sees the finished code — and keep one check the maker cannot read at all.

**Wrong by default:**
- A gate proves only the properties it measured; everything else is unmeasured rather than verified, and those are different things. All four numbers sat in band on a figure with a limb ending in mid-air, because no number described the limb.
- Values clustered dead centre of every band are evidence of aiming, not of accuracy — work aimed at the thing scatters, work aimed at the range does not. Treat suspiciously tidy conformance as the signal to go and look at the work itself.
- Handing a gate to the thing being gated destroys the gate: the pass rate climbs and the quality does not, because passing has replaced the goal instead of indicating it. Keep at least one check in different hands from the work, and one the maker cannot read.

## Everything not named is protected. So name it, and rank it

Write down the set of things that must not change, plus this round's single priority.

**Watch for:** If you asked an AI to trim a page down by a set percentage while leaving named parts alone, what would it likely get wrong?

It will do the arithmetic you wrote rather than the arithmetic you meant, because your constraints arrive as a list of rules to satisfy and not as a picture of a finished thing. Nothing in the request tells it which loss you would have refused. So it holds every named value, takes the whole reduction out of the parts you never mentioned, and reports success honestly — every check you set is green, on work you would have sent back at a glance. Add up what you have protected as a share of the whole before you send the request, and name in plain words the one outcome that must survive.

**Wrong by default:**
- A reduction stated against a total does not spread — it lands entirely on whatever was left unnamed. Pin 46.1% of a whole, ask the total down 14.6%, and the unnamed remainder has to fall 27.1%.
- A list of what must not move is half a request; the other half is the arithmetic that list forces. Add the protected share, subtract it from the target, and read what the remainder is being asked to absorb before sending.
- Green on every number a request set is evidence about the request, not about the work. Name the one outcome a round exists for, in words, and check that outcome before any range.

## Edit a good one. Replace a bad one. Never rework a bad one

Tell when to re-specify from scratch instead of correcting, and when that rule does not apply.

**Watch for:** If you asked an AI to fix one thing in a file and it returned the whole file rewritten, what would it likely get wrong?

It rebuilds the file from its reading of it plus your sentence, and the parts you never mentioned are written from the pattern it has seen most often rather than copied from yours. Your fix will usually be correct. Attached to it, silently, are the choices it would have made if it had been writing the file from the start — renamed values, reordered sections, a default restored where you had deliberately changed one. Ask for the change as a difference against what you sent, not as a replacement, and read the parts you did not ask about first.

## Which tool, for which job

Route work by a tool's characteristic failure rather than its reputation.

**Watch for:** If you asked an AI to build a panel that reports live figures out of your own system, what would it likely get wrong?

The layout will be the pattern it has seen most, and that part is often better than yours. The figures are the problem. An empty cell reads as unfinished work, so it fills each one with a value of the right shape and the right unit, drawn from what such a panel usually says rather than from what your program computes. That is the shortest route to something that looks finished, and it leaves no trace of being a guess — a plausible figure under a real heading looks exactly like a measurement. Ask for the line of code or the record that produces each number, printed beside the number, and treat any row that cannot name one as empty.

**Wrong by default:**
- A check on a readout's words says nothing about its values — vocabulary and arithmetic are two separate audits. Three rows passed on headings and units here, and not one of the three matched what the program computes.
- A readout headed with a unit is a promise the number came from somewhere, so find what computes it before keeping the row at all. Nothing in this program computes an arrival height, in centimetres or in anything else.
- A true fragment and an invented one in the same sentence is the hardest kind to catch, because the true half is what makes the rest read as sourced. Split any sentence that mixes a quotation with a figure, and trace the figure on its own.

## A bridge that returns nothing

Reason about a boundary between two technologies where nothing comes back.

**Watch for:** If you asked an AI to make a button report why it failed to open a screen owned by another app, what would it likely get wrong?

It reaches for a returned yes-or-no, because that is how most code in the world reports an outcome and it is the shortest thing that works. The outcome is not known when the call ends, so the value it adds will always say the same thing. It will also write the success path and stop there, because success is what a service's own instructions are mostly about, and a failure path has to be asked for. Insist on one sentence naming where a failure becomes readable — and treat any boundary whose failures have no home as untested rather than working.

**Wrong by default:**
- A returned value cannot carry an outcome that is unknown when the call ends. Opening a screen owned by another app is a request that finishes later, so the honest answer at the moment of return is no answer — the outcome has to be stored where the caller can ask for it afterwards.
- A success-only listener turns every failure into silence, because a request that fails never calls it. Attach the failure path in the same change as the success path, then check that a refusal, a wrong id, and a service that will not answer each land somewhere a person can read.
- Design a boundary for the case where the far side is absent entirely, not as an exception to it. The same code here runs in a browser, on a phone without the service installed, and on a television, so every call checks the boundary exists before using it and a missing one still produces a working app.

## Server-held, cached, or gone

Say, for a synced feature, what the server owns, what is kept locally, and what happens offline.

**Watch for:** If you asked an AI to load a player's saved data from a server when your app starts, what would it likely get wrong?

It writes two states — you have the data or you do not — because that is the common pattern and the shortest thing that works. The two it quietly merges are "not back yet" and "nothing there", and only one of those permits writing over the saved copy. It cannot see which screen a person reaches first on a device with nothing stored, so it treats the race as a rare edge when it is the ordinary case. Ask it to list every state the value can be in and what each one permits.

**Wrong by default:**
- Read a state word out of the code that returns it, never out of the note above it. Here the note names one word and the code hands back another, so a reader who handled only the documented word would never see a failure at all.
- Establish what a synced feature does with no signal before assuming it retries, and write the answer where a caller will read it. Here every path does nothing and says nothing, and nothing is stored to send later, so work done offline is not delayed work — it never happened.
- A value that means both "nothing there" and "not back yet" must never be read raw, however safe the wrapper around it is. Search for every reader before trusting the safe path — one entry point reading the raw value sent players who had a saved career to a create-a-new-one screen.
- Where a service keeps only the best value it has ever seen, any quantity that can fall is frozen at its peak and can never be corrected downwards. Check a service's keeping rule before choosing what to send it, because one early high score would sit at the top permanently.

## Read the version out of the artifact

Verify what you actually shipped, rather than what you think you built.

**Watch for:** If you asked an AI to confirm which change is inside the build you shipped this morning, what would it likely get wrong?

It answers from the files it can read, and those are your project rather than your package. A project records what you intended to ship. It also treats a build that finished without errors as proof the work happened — and a step which decides it has nothing to do also finishes without errors. Ask for the identity read back out of the shipped file, with the command that read it.

**Wrong by default:**
- Read a version, a signer or a build identity out of the thing that shipped, never off a source file. A source file records what was intended and the package records what happened — here a release carried one change's label while holding another change's code.
- A copy step that finds its inputs unchanged does no work and still reports success, so a green build is not evidence that anything moved. Require every step that must run to declare what it writes, so the steps downstream are told the file changed.
- A copy leaves behind whatever it stops producing, so a destination that looks full may be holding last month's contents. Prefer a step that makes the destination exactly equal to its inputs, and list the files it should contain rather than describing them with a pattern.
- A build you install yourself and a build a store hands out can be signed with different keys, and anything that trusts the signature must be registered for each one separately. Verify signature-dependent features against the exact build the store distributes, because nothing in the project can detect the mismatch and it looks like lost user data.

## Write the test against the pattern, not the instance

Shape a check like the whole class of mistake, rather than like the one time it caught you out.

**Watch for:** If you asked an AI to stop a row that should be hidden from appearing on screen again, what would it likely get wrong?

It repairs the row you showed it and writes a check about that row, because the row is the only thing in front of it. That this is the fourth time lives in the project's history, not in the code, and nothing in the file points at it. One repair, one check is the common pattern everywhere else, so it is the default shape — and the narrowest check that goes green is the shortest thing that works. Ask for the rule that was broken, in one sentence, before anything is written. Then ask where else that rule already applies.

**Wrong by default:**
- Shape a check like the rule that was broken, not like the thing that broke — one shaped like the instance closes the instance and nothing else. Four different rows lost to the same kind of styling rule here, three of them inside three days, and only the fourth repair wrote a check that reads the whole sheet.
- Count the checks that read the layer where the mistake lives, never the checks in total. Sixty-four files of checks here held exactly four that look at the styling rules at all, three in one file and one in the other, and that was the layer all four faults lived in.
- Read a check's narrowing lines rather than its title, because one that reads as general can still be pinned on a second axis. The general-sounding rule here is applied across every styling rule in the sheet, and then a single line skips every row whose name is not the one that failed.

## Make the repo argue with you

Write the record of your own work so that you, or an AI, can check its reasoning later.

**Watch for:** If you asked an AI why a piece of your own code is written the way it is, what would it likely get wrong?

Code records the result of a decision and never the alternatives, so there is nothing in it to read a reason out of. What comes back is reconstructed to fit what can be seen: the most common reason for that shape elsewhere. It is fluent, specific, and yours only by luck. Where a project keeps its reasoning in its saved messages, it is reachable — so ask it to quote the message it read and name the saved change. A reason with no source is a guess wearing a source's clothes. If it is written nowhere, that is the finding.

**Wrong by default:**
- Read a change's purpose out of the record of the change, never out of the shape of the edit, because the same edit serves opposite purposes. The saved message here says the file was named so that the copying step would notice it had changed, and a second line exists precisely to say the step is never skipped.
- Measure a record before deciding it is empty: take the length of the middle message, not the last one you happened to read. In this history the middle saved change spends twenty-three lines explaining itself, more than nine in ten spend at least ten, and this one runs thirteen lines without describing the edit at all.
- Withdraw a wrong number where you published it and name what produced it, because a record that quietly edits its errors cannot be read back for judgement later. Four withdrawals sit in this history as saved changes of their own, and one of them counts itself as the fourth time that page had made the same mistake.
