Adds docs/defect-audit.md — sixteen entries, no code changes.
Why this is not about detekt
The obvious place to look for defects is the static-analysis output. There is nothing there: detekt reports 0 findings across 41 files, and there is no baseline, no @Suppress, no //noinspection and no tools:ignore anywhere in the repository. A detekt baseline would be an empty file, so none is proposed.
Running detekt with allRules produces 467 findings, but 402 are UndocumentedPublic* and FunctionNameMaxLength on backtick test names, two point at KDoc that is demonstrably correct (D12), and zero are in the potential-bugs ruleset. Enabling it would mean writing KDoc for 177 public properties, which is the opposite of what config/detekt/detekt.yml says its own purpose is.
The defects are in the code the linters cannot see into: OutputPublisher, both ViewModels, both Workers and MainActivity — roughly 1,200 of ~4,000 lines of main source, with no JVM unit tests at all, and the direct explanation for the ~31% coverage figure.
The device pass earned its keep by contradicting us
Four entries were driven on a physical Pixel 10 Pro XL running API 37, and are marked differently from the ones that are still inspection only. The confidence labels are load-bearing.
D1 — the one defect that was already known and deferred, the UsableSpace lint finding — did not reproduce.getAllocatableBytes measured 500 MiB smaller than usableSpace, and writing 3 GB into the app's own cache moved both numbers by the identical amount, so no cache counted as reclaimable at 66% free. The entry keeps the falsified prediction next to the measurement that killed it, because that is the useful part. Its fix exists but is parked: it needs a near-full-disk measurement before anyone can honestly justify it.
D13 was found by the device pass and is the most serious entry. Work interrupted by process death fails terminally instead of resuming, on a natural dispatch 119 seconds after kill -9, with reschedule = false and output Data of X'ABEF000100000000' — a header with zero entries, so the UI shows a failure with no message. That contradicts ConversionWorker.kt:33 ("the queue survives process death"), the app's stated reason for choosing WorkManager.
Conventions used here
Entry bodies describe each defect as found and are deliberately not rewritten as fixes land. This is the record of what was wrong, not a changelog; the summary table carries fix status.
Every entry states its confidence and, where it was not reproduced, its forcing condition — so an unverified entry is a task rather than a hunch, in the same style as docs/api-37-emulator-crash.md.
D15 and D16 were found while fixing other entries and are recorded rather than folded in silently. D16 is the one worth reading: two individually correct fixes compose into a gap that neither of them owns.
Ten of these are fixed in #8. D5 and D7 are in progress. D1 is parked.
Adds `docs/defect-audit.md` — sixteen entries, no code changes.
## Why this is not about detekt
The obvious place to look for defects is the static-analysis output. There is nothing there: detekt reports **0 findings** across 41 files, and there is no baseline, no `@Suppress`, no `//noinspection` and no `tools:ignore` anywhere in the repository. A detekt baseline would be an empty file, so none is proposed.
Running detekt with `allRules` produces 467 findings, but 402 are `UndocumentedPublic*` and `FunctionNameMaxLength` on backtick test names, two point at KDoc that is demonstrably correct (D12), and **zero** are in the `potential-bugs` ruleset. Enabling it would mean writing KDoc for 177 public properties, which is the opposite of what `config/detekt/detekt.yml` says its own purpose is.
The defects are in the code the linters cannot see into: `OutputPublisher`, both ViewModels, both Workers and `MainActivity` — roughly 1,200 of ~4,000 lines of main source, with no JVM unit tests at all, and the direct explanation for the ~31% coverage figure.
## The device pass earned its keep by contradicting us
Four entries were driven on a physical Pixel 10 Pro XL running API 37, and are marked differently from the ones that are still inspection only. The confidence labels are load-bearing.
**D1 — the one defect that was already known and deferred, the `UsableSpace` lint finding — did not reproduce.** `getAllocatableBytes` measured **500 MiB smaller** than `usableSpace`, and writing 3 GB into the app's own cache moved both numbers by the identical amount, so no cache counted as reclaimable at 66% free. The entry keeps the falsified prediction next to the measurement that killed it, because that is the useful part. Its fix exists but is parked: it needs a near-full-disk measurement before anyone can honestly justify it.
**D13 was found by the device pass and is the most serious entry.** Work interrupted by process death fails terminally instead of resuming, on a natural dispatch 119 seconds after `kill -9`, with `reschedule = false` and output `Data` of `X'ABEF000100000000'` — a header with zero entries, so the UI shows a failure with no message. That contradicts `ConversionWorker.kt:33` ("the queue survives process death"), the app's stated reason for choosing WorkManager.
## Conventions used here
- Entry bodies describe each defect **as found** and are deliberately not rewritten as fixes land. This is the record of what was wrong, not a changelog; the summary table carries fix status.
- Every entry states its confidence and, where it was not reproduced, its *forcing condition* — so an unverified entry is a task rather than a hunch, in the same style as `docs/api-37-emulator-crash.md`.
- D15 and D16 were found *while fixing* other entries and are recorded rather than folded in silently. **D16 is the one worth reading:** two individually correct fixes compose into a gap that neither of them owns.
Ten of these are fixed in #8. D5 and D7 are in progress. D1 is parked.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Adds
docs/defect-audit.md— sixteen entries, no code changes.Why this is not about detekt
The obvious place to look for defects is the static-analysis output. There is nothing there: detekt reports 0 findings across 41 files, and there is no baseline, no
@Suppress, no//noinspectionand notools:ignoreanywhere in the repository. A detekt baseline would be an empty file, so none is proposed.Running detekt with
allRulesproduces 467 findings, but 402 areUndocumentedPublic*andFunctionNameMaxLengthon backtick test names, two point at KDoc that is demonstrably correct (D12), and zero are in thepotential-bugsruleset. Enabling it would mean writing KDoc for 177 public properties, which is the opposite of whatconfig/detekt/detekt.ymlsays its own purpose is.The defects are in the code the linters cannot see into:
OutputPublisher, both ViewModels, both Workers andMainActivity— roughly 1,200 of ~4,000 lines of main source, with no JVM unit tests at all, and the direct explanation for the ~31% coverage figure.The device pass earned its keep by contradicting us
Four entries were driven on a physical Pixel 10 Pro XL running API 37, and are marked differently from the ones that are still inspection only. The confidence labels are load-bearing.
D1 — the one defect that was already known and deferred, the
UsableSpacelint finding — did not reproduce.getAllocatableBytesmeasured 500 MiB smaller thanusableSpace, and writing 3 GB into the app's own cache moved both numbers by the identical amount, so no cache counted as reclaimable at 66% free. The entry keeps the falsified prediction next to the measurement that killed it, because that is the useful part. Its fix exists but is parked: it needs a near-full-disk measurement before anyone can honestly justify it.D13 was found by the device pass and is the most serious entry. Work interrupted by process death fails terminally instead of resuming, on a natural dispatch 119 seconds after
kill -9, withreschedule = falseand outputDataofX'ABEF000100000000'— a header with zero entries, so the UI shows a failure with no message. That contradictsConversionWorker.kt:33("the queue survives process death"), the app's stated reason for choosing WorkManager.Conventions used here
docs/api-37-emulator-crash.md.Ten of these are fixed in #8. D5 and D7 are in progress. D1 is parked.
🤖 Generated with Claude Code