The summary in exports/<platform>.json read '82 hashes corrected' and '854 additions left out'. It now counts what the written file carries, in the exports page's terms (hashes updated to the emulator source, required flags aligned with the code). Files the format cannot carry, withheld updates and content-found labels are printed on a line of their own and stay in the report's counts.
The manifest now carries system_names, each system's native_system names from the platform's own file, and --system, its suggestions and --list-systems read them: --platform batocera --system psx installs sony-playstation.
docs/ is gitignored and was built after the tests, so the three guards
that read rendered pages skipped on every CI run. The site is generated
first, the suite runs whatever that step returns, and the guards fail
in CI when the pages are absent.
refresh_stale ran a parent's rescrape and its children's truth and
export jobs side by side, so a child read the parent mid-rewrite. A job
now starts once the rescrapes of its ancestors have ended, and a child
no row names gets its export rewritten after them. A rescrape chain ends
on verify.py --platform, whose summary closes the report.
download_external stayed in generate_pack, outside the offline switch: it fetched under --offline, wrote to the system /tmp and held the body in memory. It now lives in packresolve, honours the switch, streams into the repository tmp/ and hashes on the way.
verify --emulator listed only OK destinations, while the emulator pack
also writes a copy whose hash the profile contradicts, loose or archive.
pack_destinations now records the paths the replay places, and both
branches resolve through packresolve.resolve_file as the pack does.
When two cores declare different values for one file, the truth keeps the first and marks the entry hash_conflict. Its hashes pair an entry and never replace the platform's: armsx2's GameIndex.yaml no longer overwrites System.dat's line for LRPS2.
Bios::Scan takes Zip::Md5Composite for every .zip (Bios.cpp:64-68). The resolver already honoured zip_digest: composite, but digest_verdict accepted the container's own md5 first, so the pack check passed bytes Recalbox marks red. The page described that order.
Step 1b read 18 924 held files and members for 7 407 distinct contents and ran every transform on each read. A content already transformed is now skipped (the first path still keeps the match), the power-of-two crc shift operators are squared once for the pass instead of once per length, and a group pairs members by the sizes it holds. derivable.json and the step output are byte-identical, 33.4 s to 25.1 s.
export_native and cross_reference read data/ without the shared hold, so
a refresh could swap a tree mid-walk. hold_data_dirs skipped a cache not
yet on disk, so a refresh could install it between verify and the packs
of one pipeline run. Both now hold every registered tree.
BizHawk is both a platform and a profile display name: --from-md5 with --platform bizhawk and with --emulator bizhawk wrote one file in two layouts. The emulator mode now carries _Emulator in the name.
FileConsumers resolved platform declarations with their destination as
a hint, which the builder, the manifest and verify never pass. RetroBat's
63 mame/hash lists and the curated gc-ntsc-12.bin credited another copy
than the one the manifests install.
verify resolved platform entries with common.resolve_local_file and put
the slot override first, and the region pass added a destination hint.
packresolve.resolve_declaration now serves the builder, the manifest,
verify and the region pass: release assets cached for a file absent from
bios/ count as shipped in all four, and a release asset keeps its answer
over a slot override.
The refusal tables compared each flag with its default value, so --source full and --group-by system passed beside the modes that refuse every other value. Both now default to None and take their effective value after the checks, in generate_pack and pipeline.
The Dolphin-MMJR codehandler.bin and armsx2's game_controller_db.txt left
the collection, and the two guards that read them skipped on every run.
Both now build the trees they need, and the guards still reading the
repository fail instead of skipping when their data is gone.
The FBNeo import recorded the listing's blob sha for bytes fetched from a
branch URL a CDN caches, and --fetch <revision> still read the tip. The
revision is now resolved to a commit, listed and downloaded there, each
DAT is held against its blob sha, and a DAT read at a named revision is
labelled with it without replacing the tip's recorded blobs.
No platform YAML holds an md5 shorter than 32 characters: the Batocera scraper completes one when a single collection md5 starts with it. The pages said some entries carry 29 characters and that scrapers store them as-is.
The database keeps one path per content, so a system placed by the
copy deduplication kept linked to another maker: nintendo-nes and
nintendo-fds to Arcade > FBNeo, sega-mastersystem and
sinclair-zx-spectrum to Other > zesarux. Every folder holding a copy now
votes, an id that spells one console folder links to it, and a maker's
folder outranks the folders that file copies by emulator.
EROFS was advised like a permission refusal, and sudo fails the same way on a read-only mount. The advice now reads the mount flag and says to check and remount the card or drive.
Every US image now lists latin-america beside north-america, so the
equality filter matched nothing and the test skipped. It filters by
membership and fails instead of skipping when the profile stops ranking
US candidates.
BusyBox wget has no --https-only and refused the whole command, so a curl-less Alpine system (the RomM image) could never bootstrap. The flag is passed only when wget lists it.
EmuDeck's macOS build reads ~/.config/EmuDeck/settings.sh like the Linux one (functions/all.sh:3-4,24 and vars.sh:2 at dddb3022). The settings and ~/Emulation/bios rules now run on darwin too.
A module-level alias written with a PEP 604 union compiled under 3.9 and
raised TypeError when check_release_assets.py, and check_release.py
through it, were loaded. The floor test now imports every module under
scripts/ with a real 3.9 interpreter, which CI gives pyyaml and
jsonschema.
The pipeline locked only --output-dir while it also writes database.json,
install/, README.md and docs/. A run on a scratch output directory could
start beside the run on dist/ and rewrite them. Every run now holds the
repository's .lock first.
_patched_entry read '#{ ... }' lines as live items, found no match in the model and gave up, so atarist was written again whole (52 lines changed for 3 additions). A commented item is now left as written.
--system compared the raw id, so the normalized spelling printed by verify.py --list-systems selected 1 Saturn profile of 11. The hint now counts the spellings that listing prints.
release_record rebuilt nothing from the name builder: a substring match
took RetroArch_v1.22.2_AndroidArm64V8A_BIOS_Pack.zip for the RetroArch
pack. The record now refuses any pack that is not a registered platform's
full pack and stores the platforms each pack serves, which the readers
use instead of the pack name.
check_release_assets.py built a tuple[..., int | None] alias at import, a TypeError on the 3.9 floor tools.md states, and check_release.py imports it. A test reads every script for unions and match statements evaluated at runtime.
RomM lists the platform firmware folder without recursing and looks each file up as slug:file_name (models/firmware.py, base_handler.list_files at 5.4.0). An addition named with a directory (fuse/48.rom) is not written.
--detect-new-files and --watch-hashes were printed only by the text report and refused with --json, so check_freshness --profiles never saw a name or hash the cited code gained. Both now live on the report, and check_freshness passes them and marks such a profile stale.
The render writes one biosFiles item per path, as checkBios reads one md5 per item. A second addition at a held path was dropped from the file and still counted added (78 announced, 75 written).
Each MT-32 entry cites the SHA-1 line and the ROMInfo line of one
mt32emu.h. dosbox_pure wrote them under two repository words, so the
ROMInfo line was self-checked alone and read MOVED onto the SHA-1 line.
Both now sit in the port that carries the file, and in the upstream for
dosbox-pure-unleashed, whose tree has no src/gui.
A recale that lands on a line a sibling part already cites goes to
review, and a part settled by its anchor keeps that anchor's repository.
cross_reference --platform read bundled from the profile alone and never
read unread_on. It now calls validation.installed_with_emulator and
unread_on with the platform key, as verify does, and a file installed
with its emulator is reported as bundled whether held or not.
DecideNAND (MelonDS.cs:684-706) keeps, for a DSi title, only the NAND
slots whose bit is set in header byte 0x1B0. The six DSi_Nand entries
now carry that territory, so --region keeps one 240 MB image, not six.
AUS answers oceania, as every other Australian slot does.
Systems a core covers without a file were added while walking a set,
so the hash seed placed them and two runs wrote dist/truth/<platform>.yml
in two orders. The normalized system map was built from a set too.
The buildbot serves nicaiemu_libretro (frozen at 4c9b73f7, last built
2026-10-16 05:15) beside coolbaremu_libretro. They are two builds: each
reads its own orientation list from the system directory, so both
profiles stay, each with its own core name. Both read the same .cbe
archives from the same workspace, which builds a standalone binary at
both revisions: one system id and one build type for the two.
Ranking contested core claims sorted the whole gap report, which
reordered every manifest. Only the claims on one contested path move now,
grouped where the first of them stood, and two spellings of a path are
one group where the filesystem folds case (RetroBat's 64DD_IPL_DEV.bin).
An entry without a destination (EmuDeck's system:md5 whitelist) no
longer counts as a pack path in verify, and step 5 prints one status for
counts and paths together. A full offline run compared all 12 archives
and 930 emulator packs path by path, every one equal.
Thirty one-line labels in verify.py, generate_site.py and
generate_truth.py named the statement under them ("# Load registry",
"# Copy branding assets"). Comments that give a reason stay, as do the
module section headers. A test reads the three files for new ones.
The which-pack page announced itself ("This page picks the right pack
for a setup") before naming a pack. The sentence goes, the pointer to
Getting started stays.