profile_sync --strict exits 1 when a profile has refs to review. The
validation workflow runs it on every profile a pull request or a push to
main adds, so a profile whose refs miss their value no longer lands green.
A count of assets misses a failed upload beside a stray file and a part
re-uploaded from an older build. check_release.py holds each asset's
GitHub sha256 against SHA256SUMS.txt and release.json, and the release
process publishes the draft only on its clean exit.
Lakka builds an image named switch while RetroArch files switch as
nintendo-switch. One canonical name taken across the registry put
Lakka's model under RetroArch's target. generate_truth now writes each
platform under its own target name, and diff_truth --target reads it.
Lakka builds LIBRETRO_CORES minus the exclusions each project, device
and GLES image adds. Without a list, --target refused Lakka and its RPi1
pack carried firmware for cores the image does not build.
The Wanted page listed 79 dumps the collection already produces,
such as a Kickstart's byte-swapped words or the halves of a C128
character ROM. The pipeline now records which missing dumps can be
derived, in derivable.json, and the page moves them to a section of
their own that names the held file and the transform.
Several DATs list the same dump, as No-Intro's three FM-7 sets do
for their system ROMs, and the provenance report counted it once per
listing: 2485 missing announced for 2284 distinct dumps, on the site
as well. Dumps are now counted by identity, each keeping the DATs
that list it.
argparse completed a prefix into the only flag starting with it:
slots.py --emulator read profiles from a directory named after the
emulator and reported nothing, and pipeline.py --all built every pack
variant. Every parser now refuses an abbreviation.
auto_fetch stopped on any profile listing several accepted hashes,
and filed a fetched file in the first folder sharing one word of its
system, overwriting a same-named revision held there. Hash fields are
read as sets, a folder must match the whole system slug, and another
revision goes to .variants/ beside the file already held.
The EmuDeck pack README promised standalone keys set up
automatically, which needs --standalone-copies, and named a Citra
folder the installer never uses. The RetroArch README gave Windows
users a shell command only, and RetroPie's sent a PC to a Linux
partition it cannot open. Each guide now matches the installer.
The installer looked for retroarch.exe in C:\RetroArch-Win64 and
Program Files\RetroArch only, so a RetroArch under Program Files\
RetroArch-Win64, in a Steam library on another drive, or in an
installed ES-DE's Emulators folder went undetected. All three are
searched; Steam libraries come from Steam's own library list.
The system pack parity test guarded against an empty parse by
requiring files to stay missing, so it would fail once the MSX and
Neo Geo gaps are collected. It now requires both reports to have run
and the pack's own missing count to match the lines it parsed.
romset_recipes --identify --write ran only the identification, so
the reconstructions --write saves were never computed and the run
exited 0 having written nothing. --write now implies --missing.
Under --key the platform restriction was not applied, so dolphin-sys
was downloaded again for RomM, which never reads it. A key whose
platforms exclude the one named is now refused.
Named files beside --changed were replaced by the git diff, and the
run reported no changed files about files it never read; --json
beside --markdown dropped the markdown. Both combinations are now
refused, and an empty JSON run prints an empty list.
The mode and flag matrix only tests refusals, so a flag verify
accepted and ignored, --verbose under --json, passed it. Each mode is
now checked to change its output when that flag is given.
The build scripts are documented as needing only the standard
library and pyyaml, and the Windows downloader none of it, but CI
installs both pyyaml and jsonschema's tree, so a stray import would
pass everywhere it is tested. The suite now scans every import and
runs the downloader with yaml unavailable.
install.py held a local it never read and, since the registry path
rules, a loop variable shadowing the platform module, so the py38
ruff gate the installer is held to failed. Both are fixed, and the
suite reads the two rules from the syntax tree since CI has no ruff.
A failed rev-parse in the MAME clone returned an empty commit, which
was cached for a day and let the merge advance the profile's version
and refs while its upstream pin stayed on the previous release. The
scrape now stops instead.
The deterministic rebuild raises on a member whose bytes fail their
CRC, and the builder caught it and copied the damaged archive into
the pack, expecting verify to flag it, which nothing did. The run now
stops on it and names the file to restore.
The registry declares where each platform keeps its BIOS and every
manifest publishes it, but the installer detects before it fetches
any manifest and never had RetroPie, BizHawk, RetroBat's paths, or
EmuDeck's default folder. It now carries the registry's path rules,
held equal to the registry by a test.
The registry names one path per standalone copy for every platform,
but each manifest places the file its own way, so the installer
looked where the pack had put nothing and copied nothing: EmuDeck's
aes_keys.txt for Azahar, Batocera's switch/prod.keys. Each manifest
copy now names its source, which the installer validates and reads.
The documented command, discover tests, makes tests/ the top level,
so tests/__init__.py never ran and the variable it sets to keep tests
offline was missing from every local run. Every documented and CI
run now passes -s tests -t ., and a test requires it.
The test simulates a miscompute on a real held file. Since the
manifest hash cache is keyed by the expected sha1, a result another
test verified for the same file is a valid hit, and in a single
process run the simulated miscompute was never hashed. The class
starts from an empty cache.
The site deploy still triggered on seven scripts it stopped importing
when the pack and site modules were split, and on the test suite it
never runs, so a builder-only push rebuilt the site for nothing. The
triggers now match the build's import closure in both directions.
A test isolated the extra collection by patching
resolve_platform_cores on packextras with create=True, while the
module imported it inside the function, so the real resolver ran.
The import is now module level, the test checks its stub was called,
and no test may create the attribute it patches.
The cache of manifest hashes was keyed by the database's id without
holding the database, so a freed database replaced at the same
address got another scan's verified hashes back. The key now carries
the sha1 that database expects for the path.
An export wrote into its platform directory without removing what an
earlier run left there, so RetroPie scriptmodules and RetroDECK
manifests the current truth no longer corrects stayed beside a
report saying they were untouched. Files the run does not produce
are now removed first.
The RomM export fetched known_bios_files.json and never read it: the
fixture was rebuilt in our own order, so three corrections came out
as a diff of the whole file and a key we had not transcribed was
deleted. The export now starts from RomM's file, keeps its order and
fields, and only changes the values it corrects.
libretro indents 39 rom lines of System.dat with spaces. The export
read tab-indented lines only, so those left the base and came back
re-rendered at the end of their section, and one our transcription
lacked would have been deleted. Any indentation is read and kept.
The export only indexed firmware BizHawk leaves unhashed on purpose,
per-console dumps declared with SHA1Checksum.Dummy, and could only
apply that hash to a same-named call of another system. Every hash
the file states is BizHawk's own, so nothing is rewritten and no
correction is reported.
A truth that accepts several sizes left an export with no size even
where the platform states one of them for the same dump, and RomM's
fixture then received the string None, which RomM cannot parse. A
size list now admits the platform's own size, RomM keeps its value
when nothing settles one, and validation requires a number.
ep128emu loads brd.rom when the content name carries the TOSEC tag
[req brd-rom], whatever the user's country, as it loads hun.rom for
[req zrom]. The Germany region on brd.rom described a territory
choice the code never makes.
NP2kai's two rhythm engines open different spellings, lowercase for
the built-in one and uppercase for fmgen, and nothing folds case,
so the uppercase names are files of their own rather than aliases.
fmgen's 2608_RYM.WAV fallback is opened without the directory and
is no longer claimed under np2kai/.
Six of the eight shared groups injected nothing: three were
included by no platform, three repeated System.dat entry for entry,
so a correction made in them reached no pack. They are removed with
their scraper wiring, and a test requires every remaining group to
contribute an entry somewhere.
The gap report took a file's libretro path even where the platform
runs the standalone build, so it named Cemu/keys.txt for Batocera
while verify named keys.txt, and settled the declaration on a slot
the platform never fills. Both now read standalone_path there.
The np2kai shared group declared font.rom as a second required file
while NP2kai reads it only when FONT.ROM, which System.dat already
declares, is absent. Case-sensitive platforms packed the font twice.
A shared group may now add a second spelling only where some code
opens that spelling itself, as the two rhythm engines do.
The export cache wrote an original's bytes, then recorded the URL
that produced them under a separate lock, so two runs wanting two
revisions of one file could pair one's bytes with the other's URL.
Both now change under the one index lock.
The pipeline held its output directory for the run but let each step
lock the data directory trees on its own, so another session's
refresh could swap a tree between the packs and the manifests. The
trees are now held shared from right after the pipeline's own
refresh until it exits.
Packs were opened on their final name, so a rebuild truncated the
previous one and a build that failed half way left a readable ZIP
of part of the files. Each pack is now written beside its name and
replaces it only when the write finished.
A tag looked up before it was pushed was cached as absent for good,
so a profile pinned on that release was never recognised as frozen
on it. The absence now expires; a tag found stays cached.
The commit list behind the triage drift score was cached for good,
so pushes upstream never moved a profile up the triage. It now
expires like the other moving answers.
Emulator mode reduced region groups without knowing which files the
collection holds, so a requested-region image nobody had collected
withdrew the held one and left the slot empty. Platform mode already
kept it; both emulator readers now pass the database.
The platform's files and the core extras for the Master System sat
in two region groups, sega-master-system and sega-mastersystem,
reduced apart. A US pack kept the declared Japanese BIOS. A core
extra's system group now takes the platform's spelling, in the pack,
the report and the installer's manifest fields alike.
The doubled-path tests import validate_schemas, which needs
jsonschema; without it the suite erred instead of skipping, which
the optional dependency check refuses.
generate_truth named a narrowed model's directory after the target
as typed, so three aliases of one target wrote three copies, and
the pipeline rebuilt the slug by hand to diff it. One function now
names the directory from the canonical target for both.
A target was canonicalised once for the run and kept as typed under
--all when platforms file it differently, so the --all build and a
--platform check named the same Batocera pack two ways. Each pack,
group, manifest and expected name now takes the target as its own
platform files it.
--platform --system --one-per-slot was refused with a message
asking for --platform, which the command carried. The refusal now
names --system or --emulator, and every refusal in the mode matrix
is checked never to ask for a flag it was given.
generate_pack split --platform on commas to validate it, then used
the whole string as one platform and crashed. Every script takes a
single platform; a list is refused with a message pointing at --all.
verify accepted --verbose beside --json and dropped it in every
mode, so the output was the same with or without it. The JSON now
lists every entry with its ground truth under --verbose.