A destination can be declared by more than one system, bare in one and
hash-constrained in another. generate_pack resolves that with
_preferred_entries so the constrained sibling claims the destination;
generate_manifest never did, and named whatever answered to the name.
RetroDECK's bios/d2fdc.zip is the case: declared with an md5 in the
arcade system and bare under apple-ii. The pack carried the right
archive, 262 bytes reached through the MAME clone map, while the
manifest sent install.py to a 256-byte Apple II ROM. Downloading the
ZIP and running the installer gave different files.
A test now hashes every manifest entry a platform pins and compares it
against what the platform declares: 3295 entries, on the platforms whose
frontend reads the bytes. It accepts any of several declarations for one
destination, the member-composite MD5 Recalbox pins for arcade archives,
and Batocera's 29-character prefixes. Reinstating the old manifest entry
fails it.
Pinning every member's metadata made packs reproducible and took the
executable bit with it. The RetroDECK pack ships the two Voxatron engine
binaries, and extracted at 644 they cannot be run.
Git records the bit, so reading it from the source file keeps a pack the
same from any clone. Nothing else about the source's mode reaches the
archive: 2569 members ship at 644 and 942 at 755, which is what the
builder produced before the pinning.
Nothing caught this. The comparison that proved the pinning inert
checked member names, CRCs and sizes, and mode is none of those. A test
now builds a runnable payload and asserts it survives extraction.
RetroDECK rebuilds to the same bytes twice and passes its integrity
check, 2008/2008 baseline and 1551/1551 cores.
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.
The markdown report wrote to a directory named after the tooling that
happened to produce it. It takes --report-dir now, defaulting to
reports/, so nothing in the tree names anything but the project.
Em-dashes replaced throughout the sources and tests, rephrased rather
than swapped for a comma where the dash carried an apposition.