parse_db silently skipped a row that had no symbol name and accepted any
text as a NID, so a malformed database produced Unknown stubs and
technical-debt lines for NIDs that did have a name, with no hint that the
database was at fault. Reject such rows with a path:line error, the way
parse_nids already rejects malformed NIDs.
Removing the generated source and output directories ignored errors, so an
undeletable stale registry survived and was audited with the new run's
registries. Report the failure and return 2 before conversion; remove a link
in place of a generated directory instead of following it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Auditing a title before its first run takes several manual steps from
GAME_TESTING: unwrap eboot.bin and every module the title ships with
unwrap_self.py, convert them with relinker --registry (with
--module-dir for Unity's Media/Modules and Media/Plugins, and for
modules beside eboot.bin), and pass every registry and module folder
to import_audit.py. Missing one module folder turns its imports into
false "absent" entries.
tools/audit_dump.py <app0> --relinker <path> --libs <dir> does those
steps into a temporary directory (or --work <dir>), reading the dump
only. A module that is already a plain ELF is copied; one that is
neither a SELF nor an ELF fails the run. It returns import_audit's exit
code, or 2 when unwrapping or conversion fails.
audit_dump covers the module folders and the module-dir list, a
refused module, the relinker command line, the arguments passed to
import_audit, a relinker failure and the argument errors.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
test_output_directory_that_cannot_be_written_is_named called progress.main
without a root, so main measured the whole checkout before reaching the
output directory the test checks: 12.8 s of the file's 14.1 s here, and
31 s on windows-latest. It now goes through run_main with
library_snapshot(), like the other progress.main tests in its class;
write_site still runs and fails on the file in place of the directory.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The debt-file rule read only the first line of an added entry, so an
entry whose first core/ link sat on an indented continuation line was
rejected in docs/dev/debt/<name>.md and accepted in the index, the
opposite of what debt_split.py does with the same entry. The rule now
joins the continuation lines as debt_split.parse does before naming
the file.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
A compound statement that is not a conditional, loop or lambda always runs, so its calls are collected like the body's own and its exits stop the scan. An export whose only throw sits in such a block stays a stub and fails a strict audit.
A conditional return, co_return or goto before the throwing call leaves a path that succeeds, so the audit stops collecting calls at it and the export, or a helper it calls, is partial. A guard that throws still leaves every path throwing, so it stays a stub.
The previous check called any stub call inside an #if block partial,
so an export that throws in every #if, #elif and #else alternative was
reported as partial. Each combination of preprocessor branches is now
expanded into its own body, an #if without #else gets an empty
alternative, and the export is a stub when every combination reaches
the throw. More than 256 combinations in one function, or unbalanced
directives, are reported as errors.