norefs --fix prints the findings, then applies the ones whose verdict proves them safe and saves the files:
export keyword. A dead export is removed whole, together with every import and re-export specifier that forwarded it — nothing dangles in a barrel. A dead default export takes its default imports with it: import lid, { shelfCount } from './box' keeps the rest of its clause, and an import the default binding was holding up alone goes entirely. A dead member is deleted; a dead parameter property (constructor(private readonly dead: number)) only loses its modifiers and stays a plain parameter, so the constructor signature and every new call site keep working.write-only, contract, and shadowed findings wait for --fix-unsafe (it implies --fix). These are claims the analysis cannot prove — a wire format, a value alive through a duplicate type — and no type checker catches a wrong deletion. Review that diff with care.write-only member is retired with the writes that prove it — the property assignments the evidence cites, any local whose last reader they were, and the dependency entries that named that local — because deleting the declaration alone would leave the value computed into a shape no named type describes: the finding, made undetectable by the next run. useMemo(() => ({ track }), [track]) is the case that needs all three; stop at the write and the dependency array keeps the dead computation alive for the type checker and for norefs alike. When one of those writes cannot be removed on its own — a spread carries members beyond this one — the whole finding is kept and the write is named: Kept `extra` (src/payload.ts:2): the write at src/payload.ts:9 is why this isn't safe..d.ts is left because its export keywords are what make it a module rather than a script of globals: remove the last one and every declaration beside it goes global, and the file still compiles, so no type check would catch it. An emptied property or const binding is the same answer: something still reads it, and that read now reaches nothing.--fix only touches what is reported, so --only, ignore globs, and suppression comments limit the fixes the same way they limit the findings.--no-verify skips the check when the double type-check costs more than you want to pay. The check covers what the program holds, and a file the tsconfig excludes is not in it — which is why the analysis reads the imports of the code beside the program before a finding is ever made, rather than leaving the type check to catch a deletion it cannot see.--verify-command is the honest witness for them..d.ts declares, or one you named with boundaries — reports where the same channel reappears: deleting the wrapper strands that far-side handler, which no reference-based analysis will ever flag. The note only appears when the fix deletes every sender of that channel — one surviving sender and nothing is stranded — and the handler also gets a stranded finding of its own, so it is visible before the deletion hides it.--verify-command "npm test" raises the bar: after the type check passes, the candidate files go to disk, the command runs, and the originals come back before the verdict. A fix your test suite rejects is held back like any other — the diff you get is one your own tests already passed.Review the diff before you commit. The emptied-type findings point at the leftovers that need human judgment.
To see the diff without touching anything, run norefs --fix --dry-run. It applies the full fix — cascades included — to the in-memory project only and prints one unified diff per file. The exit code stays 1, so it also works as a strict CI check.
The fixing flags interact: flags.md is one page on what every flag and every combination does to a working tree, and where the exit codes are pinned.