The verifier and the builder each resolved the profiles a run names, in
thirty-five lines that differed only in how they failed: one exits, the
other returns empty-handed. An alias is the same binary under another
name and a launcher only starts an emulator, so neither has requirements
of its own, and both refusals have to say the same thing.
common raises now and each caller chooses its own ending. Six tests hold
the refusals, one of them reading both sources so a copy cannot grow
back. The manifest's core-complement phase comes out of generate_manifest
in the same pass, 60 to 34.
Verified inert: manifests identical entry for entry, and the Handy pack
rebuilds to the same bytes.
find_undeclared_files decided per entry whether a core requirement can
be a gap, through a chain that mixed two skips whose difference is easy
to lose. Some record the requirement as settled so no other profile
reconsiders it; the rest leave it open, because the same file can be
libretro-only in one profile and standalone-only in another and the key
carries no emulator.
The chain returns a named verdict now, complexity 60 to 45. Twelve tests
cover the verdicts and two more cover the distinction end to end: a
standalone-only entry seen first must not answer for the profile that
needs the file. Collapsing the two skips into one passes every other
test in the suite and fails that pair.
Four sites still compared the verification mode to a literal after the
module owning that policy existed. One of them mattered: an unrecognised
mode fell through to MD5 verification while compute_severity was scoring
it as existence, so a typo in a platform YAML produced a report whose
checks and severities described different platforms.
The mode is normalized once per run and the consumers ask for what they
need. A test reads the sources and fails on a literal comparison, so the
next consumer cannot quietly grow a fifth copy.
The gap analysis answered from the name index, so a core extra whose
local copy contradicts its declared hash counted as held. Under a
digest mode the builder drops exactly that file, so the coverage report
described a pack that would not contain it: seven files across
Batocera, Recalbox and RetroBat.
An entry that states what its content should be is now resolved by
content; the name still answers for entries that declare nothing to
check against, and existence mode is unchanged because there the
frontend never opens the file and the pack does carry it.
verify.py and generate_pack.py must reach the same verdict on the same
file, and CLAUDE.md calls any divergence critical, but each spelled out
mode == 'existence' in its own words. Both now ask nativemode whether
the frontend reads the file's bytes, which is the one fact the rest
follows from, and a test holds the two answers together.
The BaseScraper contract the wiki asks contributors to implement had no
caller anywhere, so nothing proved compare_with_config, has_changes or
test_connection still worked.