--- name: audit description: Audit one area of Modbux against the eight criteria and write the findings to tmp/AUDIT-.md, changing no code. Use when the user asks to audit or assess an area, or to re-check one after changes. Do NOT use to review a branch or your own diff — that is /code-review; and do NOT use to execute a finished audit, which is a separate session with fresh context. --- # Audit one area **Read-only with respect to code.** The only file this writes is `tmp/AUDIT-.md`, which is gitignored. Every improvement you spot becomes a finding, including the one-line obvious ones. ## The areas Split by what the code shares, not by directory size. One area per run. | area | what it is | | ----------- | ------------------------------------------------------------------------------------ | | `modbus` | `main/modules/modbusClient.ts`, `modbusServer.ts` and `modbusServer/` | | `boundary` | `main/ipc.ts`, `preload/`, `shared/types/ipc.ts`, `main/state.ts`, `main/windows.ts` | | `shared` | `shared/` minus `types/ipc.ts`: schemas, migrations, pure helpers | | `stores` | `renderer/src/context/` | | `client-ui` | `renderer/src/components/client/` | | `server-ui` | `renderer/src/components/server/`, `components/shared/`, `containers/` | ## Before you start Read `CLAUDE.md`, `CONTRIBUTING.md` and `src/__tests__/conformance.test.ts`. The suite already asserts thirteen conventions. **Do not report what it asserts** — it is green, so those are closed. Audit what a test cannot see. ## The eight criteria Apply all eight. Do not merge them and do not skip the cosmetic ones. 1. **What the conformance suite cannot see.** The store-versus-component IPC rule, the folder-per-component judgement, and anything else CONTRIBUTING states as prose under _The rules no test can see_. 2. **Duplication.** Search for the shape, not the name. Two functions doing one job often share no word, so a search for what one is called returns neither. Report identical copies too, because they diverge later. A duplication claim carries its own burden of proof, under _Verification_. → [WHY: a meter is a claim too](./references/a-meter-is-a-claim.md) 3. **Dead code.** Unused exports, unreachable branches, config entries pointing at deleted paths, comments naming files that are gone. 4. **Deferred comments.** Every `TODO`, `FIXME`, `for now`, `later`, `until we`, with its exact location and text. 5. **Test coverage.** What is _not_ covered. Think like someone with a field device, not like the author: a unit id of 0 and of 248, an address at 65535 with a data type needing four registers, a serial port that disappears mid-read, a config file from two versions ago. 6. **File size and module shape.** Four files stand clear of the rest, and the distribution is the argument rather than any number you pick: ```sh find src -name '*.ts' -o -name '*.tsx' | grep -v __tests__ | xargs wc -l | sort -rn | head ``` Propose a split only along a real responsibility boundary, never on length alone. Count code lines and test lines separately: a file that looks like the worst offender is sometimes a thin one with a large test block bolted on, and the two call for opposite conclusions. 7. **Architecture fit.** Does this block RTU over TCP, a second client, the gateway idea, or anything `CHANGELOG.md` says is coming? 8. **What modbus-serial actually does.** For `modbus`, `boundary` and `shared`: every assumption this code makes about the library is checked against `node_modules/modbus-serial/`, not against its README. Two of this project's sharper findings came from that gap. → [WHY: read the library, not its README](./references/read-the-library.md) ## Reproduce, do not reason **Build first.** A reproduction against a stale bundle looks like a measurement and is not one: ```sh npx electron-vite build ``` Then drive it. The e2e fixtures already stand up a server, a client and a socat serial pair: ```sh npx playwright test e2e/specs/01-main/-.spec.ts ``` For a claim about the wire, a scratch spec beats reading. For a claim about a schema or a pure helper, `npx vitest run ` in a scratch test is faster than either. **A reproduced defect beats ten lines of reading.** Delete your scratch files when you are done. ## Recording a finding Every finding is a heading in this exact form, and then the fields under it: ``` ### · ` ` · criterion <1-8> · · size · reproduced ``` The ID is the area's initial and a number: `F1`, `CU-01`, `B-04`. Drop `reproduced` when you did not run it. **The heading is the form, not a suggestion.** Six agents were given the field list and five wrote the same heading; the sixth used numbered titles with the fields as bullets, and a grep for severity across the six documents found nothing in that one. The document was fine and the count was wrong. Under the heading: `claim` (one sentence) · `evidence` (the code, or the command and its output) · `proposal` · `recipe` if reproduced. **severity — what the app does to a user decides it, not how much it annoys you.** | | | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **blocking** | A user gets a wrong answer, a crash, or lost configuration. A malformed frame reaching the socket. A register read as the wrong type. Also: dead code that makes something look covered when it is not. | | **annoying** | Right behaviour, wrong construction. Duplication, a rule enforced in one of two places, untested logic. Nothing a user sees today; the next change here is where it bites. | | **cosmetic** | Neither. A stale comment, an unused export, a name that misleads. | **size — how much work the proposal is, not how large the defect is.** **S** is one file and no decision. **M** touches several call sites or needs a small decision you can make from the area alone. **L** needs a decision that is not yours: a protocol question, a persisted shape, or anything crossing two areas. **Give the exact recipe if you reproduced it** — the file contents and the command, complete enough to paste. A recipe that does not work as written is worse than none, because the next person dismisses a real defect. **Anchor on the symbol, not the line.** `modbusClient.ts readRegisters`, not `modbusClient.ts:412`. A line number is right for one commit; the symbol keeps working. The exception is inside a finding's evidence, where the line is what makes it checkable. **Do not record a file's size as a number.** A size is worth writing only as an argument: past the threshold, and here is why it is still one file. ## Also record what you checked and found sound A document listing only defects leaves the reader unable to tell "examined and correct" from "not looked at". Say what you read and what held, briefly. A claim you could not settle is an **open question**, labelled as one. Not a finding. ## Verification Every finding goes to a second agent whose job is to **break** it. That is not a reviewer looking for problems; it is handed a claim and asked to demonstrate it wrong. - **refuted** — you can demonstrate it is wrong. Give the demonstration. - **unconfirmed** — neither proved nor disproved. **Keep it.** Say what you checked and what would settle it. - **confirmed** — independently verified. **The bar for rejection is high.** A wrong finding costs one look; a dropped correct one costs a defect in a released build. When in doubt: unconfirmed. **No verdict returned counts as kept, never as rejected.** Two exceptions, where the burden runs the other way: - **A duplication claim must be actively substantiated.** If you cannot show the two are equivalent in behaviour, refute it and say how they differ. - **A claim marked reproduced must actually reproduce.** Finding one that does not is the most valuable single outcome available to you. ## What a finding is worth once it is written | part | trust | | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | the file, the symbol, the evidence | high, and verifiable | | the claim, if reproduced | high | | the claim, if only read | good | | **the recipe** | **low** — the most common defect in an audit is a recipe describing an input that does not trigger the behaviour | | **the proposal** | **low** — a guess by someone who did not read the rest of the file | | any summary or state | **low** — a compression, and compressions interpret | Open the file, reproduce, then decide. Being written down is not evidence. ## When more than one area is done **Group the blocking findings by cause before anyone fixes them.** Six areas audited in parallel produced 28 blocking findings that turned out to be thirteen causes, and three of those thirteen were invisible from any single document. → [WHY: the same bug from three directions](./references/one-bug-three-reports.md) Write `tmp/AUDIT-clusters.md`: one section per cause, naming the findings it holds, what they share, and what a fix has to settle. Keep the per-finding evidence where it is; the cluster document points, it does not copy. Check every blocking finding reaches the document: ```sh for a in ; do grep -E '^#{3,4} .*· blocking' tmp/AUDIT-$a.md | sed 's/^#* *//;s/ ·.*//' | while read id; do grep -q "$a/$id" tmp/AUDIT-clusters.md || echo "missing: $a/$id" done done ``` **A wrong grouping is a new way to be wrong.** A cluster that is really two causes gets fixed as one and half of it survives, so the refutation reviewer is asked to break the grouping as well as the claims. **A cluster's size is not the largest size inside it.** Each finding was sized inside one area by someone who could not see the others. Four of the thirteen here needed a decision that fits in no single area, which is size L whatever the findings said. ## Finish by reporting The document path, then one line per finding: severity, symbol, claim. Then stop. Proposing is the whole job, and the session that proved a defect is the worst one to fix it: it is invested in the finding and has read past everything it already dismissed.