The docstring promised "a smali tree or a directory of dex files" and delivered neither: iter_files() yielded only .smali, a direct .dex was read as UTF-8 text and regex-matched against smali syntax, and a dex-only directory yielded nothing at all. A real dex, a dex directory and a typo path all printed [total refs] 0 and a note blaming the needle, so the one tool the skill recommends for judging blast radius before patching returned a confident false zero. A .dex is now decoded through dexutil (no baksmali needed), a directory is walked for .smali and .dex alike, an archive is opened for its classes*.dex, input that cannot be read is refused with exit 2 and a reason, and every run prints [scanned] N file(s) so the two cases can never print identically again. Also corrects the recorded exit code for this defect: it was 1, not 0, which moves it from "no signal at all" to "the signal was there and the message contradicted it". Verified against a fixture whose call graph is known by construction (docs/tool-verification/fixtures/make_fixture_dex.py, committed as the evidence): the .dex and the equivalent smali tree both return 3/2/2, matching what was written. And against the real target: Ljava/lang/String;->length gives 356 refs in 2.3 s where it previously gave 0; a five-dex directory gives 9,472; a typo path exits 2. Docs updated with it: F9 records the opcode-table finding the fix exposed, F2/F3 and TOOL-VERDICTS.md are corrected, and dex-patching.md now tells the reader to look at the [scanned] line before the count.
Tool verification record
This directory is the evidence record for the apk-reverse skill measured against real
targets, kept separate from the skill itself so the Coverage statement in SKILL.md can be
checked against the runs behind it, and so a later reader can tell which claims were observed,
which were inferred, and which are still unverified.
It is not a set of target-specific notes kept for reuse, and it deliberately carries no target identity. The measurements are about the tools and the skill; they stay valid whichever APK produced them. Where a concrete figure was needed to make a point (a class count, a file size, a build time) the figure is kept and the target is named generically.
Targets measured: Flutter AOT applications, multi-dex, mid-size native library sets, no packer.
The APKs themselves are not in this repository (.gitignore excludes *.apk).
Strength labels
Every claim carries one of three labels, used the same way references/verification.md uses them:
- observed — reproduced here, with the exact command and its output.
- inferred — follows from observed facts, but the step itself was not executed.
- unverified — assumed or reported by a tool and not independently confirmed.
Index
| File | Covers |
|---|---|
TOOL-VERDICTS.md |
One verdict per tested script and external toolchain, with the independent cross-check behind it |
FINDINGS.md |
Findings about the skill itself: defects, contradictions, boundary evidence |
REPO-DECISIONS.md |
What changed in the repo as a result, and what deliberately did not |
Adding a run
Record the tools, versions and exact commands, and label each claim. Keep target-specific facts out: what belongs here is what a future reader can apply to a different APK — a defect, a measured cost, a method that did or did not work, and the cross-check that settled it.