One unparsable commit in a profile's history stopped the walk and
marked every older prose citation unknown, which refused the recale and
bump for good (lindbergh-loader, df7346b3). The revision is now passed
over unless the pins differ on its two sides while the field's
citations changed across it.
Step 5 compared (ok, total) and the core file count. RetroArch, Lakka
and RetroPie read 526/526 on both sides while verify credited four paths
below SGB1.sfc and SGB2.sfc that the pack never writes: a declared file
blocks them as a directory. verify now places the platform files in the
builder's order with the same path rules, and a user_provided or
external declaration keeps its slot on both sides, so no core file takes
it in the report only.
verify --destinations writes the paths each pack holds by the report,
and step 5 compares them with the archive members (notes and data tree
members set aside). Every emulator profile, in each build it has, is
built in memory (PackRecord, nothing read or written) and compared with
verify --emulator path by path. Cost measured here: about 1 s per
platform archive and 11 s for the 930 emulator packs.
BizHawk finds firmware by SHA-1 scan or a user specification, so the
profile is found_by content. 38 entries used the names RetroBat's
generator writes; they now carry FirmwareDatabase.cs recommended
names, and the pooled NDS firmware entry is split per option.
_fetch_asset hashed the cache copy, then hashed the path again for the
release check. A copy another run promoted in between answered the second
question, and the caller was told the cache held nothing when it held
the file asked for. Both checks now read one snapshot, taken while the
inode stays the same.
A source_commit string from before the field was keyed by mode pinned
the standalone repository, yet was read as a writing pin for every mode.
xenia-edge then saw its libretro pin move under unchanged prose, and
the recale and bump were refused.
A [Parameter()] attribute made install.ps1 an advanced script: its common
parameters took -v (-Verbose), -vb, -d and -ea before install.py saw them,
and the scriptblock form printed PowerShell's own VERBOSE lines. The
wrapper now forwards $args as typed.
The collection DAT held the one path per content the database keeps, so
auditing bios/ with it read 2,502 copies (RPG Maker, engine trees, dumps
filed under two consoles) as unknown files. It now lists every path under
bios/ that holds a file, 22,028 roms for the 22,031 files on disk, the
other three being .gitkeep placeholders.
A link already at the destination was skipped without a word and counted
as "dir not found" while the folder existed, so the emulator kept the file
the link pointed at. The link now gives way to the file, written beside it
and renamed over the name, and a name leading out of the folder is a named
failure.
On Windows a verified partial copy is closed before its rename. A second
run that claimed it in that moment truncated it and the first rename failed
with a sharing violation, reported as an unwritable folder. A complete
partial is now left whole there, the rename waits for a sharing violation
to clear, and a sharing violation no longer reads as a refused folder.
install.region_drops now skips one-file groups (:file:) and lets no
omitted match displace an installable file, as region.resolve_region_drops
does on the builder side. The property test draws both cases and
compares against the builder.
parse_repo knows only the forges profile_sync fetches from, so the eduke32,
fury, dgen, skyline and splitwolf pages showed no pinned revision and
their citations stayed plain code. The pages now build commit and file
links for voidpoint.io, Bitbucket, SourceForge and Software Heritage.
refresh_data_dirs printed one line per entry and no total, a dry run named
the cached tag only, and cutting a quoted ETag at 12 characters kept one
quote. The run now ends on refreshed / up to date / failed counts, a dry
run shows the remote tag beside the cached one, and tags lose their quotes
before they are cut.
The role a part writes (source or upstream) was kept per path, first
met first kept, so a later part naming the other role was read in the
other repository. In prose the word source made a citation external
and the word upstream was dropped. The role now travels on each part.
An element whose md5 the truth contradicts without naming another is
written back whole. It was counted as an entry the format cannot keep.
It now counts as kept, and the md5 it keeps is named as withheld.
check_freshness --only native rebuilds the truth model to compare it, and
the hash conflict notes generate_truth reports opened the freshness output.
They are dropped there, and any other stderr line still passes through.
RomM compares file_size_bytes with the entry's size before any hash. A
new hash the truth gives no size for went out beside the old size and
was reported as a size removal. The entry now stays as written and the
correction is withheld and named.
8dc485dc moved the TOS names into each platform's launchers: records,
but the tos.img notes still described the Batocera, Recalbox and
RetroBat generators and an aliases field the entry no longer has.
Data directory owners were picked among every profile, aliases included,
and an alias has no page: the 90 FreeIntv overlays linked to
emulators/freeintv_ts_overlay/, a 404. Only a profile with a page owns a
tree now, and a page lists the trees of the aliases that point at it.
The three data package notes cited Batocera's sonic3-air.mk:40, a
package recipe outside the declared repository, so profile_sync
reported it absent for good. The engine data note now cites
EngineMain.cpp by its full path.
The coreinfo area compared .info names only: a firmwareN_path added to a
profiled core's .info came out OK. check_freshness now compares each
profiled .info's git blob with platforms/_core_info.json, recorded on
2026-10-21 from the current listing, and --record-coreinfo CORE records
one as reviewed.
A directory entry was joined to the collection by its own name, so the
11,976 files under alephone's Plugins/ and the cdogs, thextech and
easyrpg trees named no emulator on their card. FileConsumers now expands
the directory and records which entry reads each file. The emulator links
on a file card land on that entry, and a directory card names its copies
and the folder that holds them.
The check walked the published keys only and skipped null lists: a
target the builder knows and the installer lacks, or a null left behind
once the builder filters it, passed. It now compares both key sets and
every value, null included.
Downloader rejects an entry without an integer size (db_entity.py:187)
and compares it after each fetch (fetch_file_worker.py:127). A new hash
the truth gives no size for is withheld and named, not written beside
the old size nor reported as a size removal.
HiscoreInit opens <g_save_dir>/hiscore.dat (hiscore.cpp:411). Without
load_from the entry was packed at bios/hiscore.dat and took the slot
mame2000 reads on RetroDECK, which now gets its sha1-pinned file.
The release-source check called any uncommitted repo_path gitignored,
pointing at the release assets instead of a missing commit, and its
skip outside a checkout could not fire. It now asks git which paths are
ignored, and a new check requires bios/ to hold no untracked file.
An element lists its cores. Its mandatory flag now follows whether those
cores need the file, and stays as written where none of them reads it.
cdi/cdibios.zip keeps mandatory=false (same_cdi does not read it), the
two amiberry kickstarts keep theirs, saturn_bios.bin becomes optional
for the three HLE cores it names.
Ten notes and comments called .info entries phantom, misleading or
incorrectly written. They now say what the .info declares and what
the code reads, and a prose test refuses those verdicts in profiles.
diff_platform compared only version, files and core lists: a rescrape
that changed launchers, dual_mode_cores or a system's core, native_id or
data_directories came out identical. Every other field is now compared,
without the revision read (source) and the source_ref lines that move with it.
Every member was deflated, archives and PNGs included. On the 2026-10-20
RetroArch pack the .zip, .7z, .chd, .png, .ogg and .pk4 members are 2.1 GB
that took 28 s to deflate again for 9.6 MB saved (0.45 %), and about 16 GB
for 72 MB across all packs. Those formats are now stored. The choice reads
the member name only, so a pack stays byte-reproducible.
The analysis and firmware blocks printed their source_ref as plain code,
dropped any list or map inside a list item (cemu's firmware title
contents), cut values at 200 characters and let a folded note's second
line leave its admonition. Citations are now pinned links and every
value is shown on its own line.
Bios.cpp:64-68 hashes a .zip with Zip::Md5Composite, the md5 of its
members in name order. A profile's md5 on an archive is the container's,
so it neither pairs with, corrects nor fills that field, and an archive
addition with only a container md5 is left out.
mkdocs was installed by the deploy alone, after the merge, and wiki/
was not a validation path: a dead link or a stray heading failed the
deploy on main with nothing in front of it. The test job now runs the
deploy's site build, and every site input triggers validation.
The summary, the provenance table and the per-emulator Missing column
summed the report's gap_missing, which counts files the emulator's own
package installs: 1,356 files to source where 609 are. They get their
own row and column now.
The repository was renamed to CoolbarEmu (PR #78) and its buildbot
recipe now builds coolbaremu. Hold the profile on 4c9b73f7, the last
revision built as nicaiemu, and document the orientation list it reads
from the system directory.
The buildbot system row was planned as refresh_data_dirs.py --key
'buildbot system', which always exits 1 on an unknown key. A data row now
runs only when its subject is a _data_dirs.yml entry; the rest is shown
for a human to add.
scraper_cli wrote a platform YAML with no system when the parse came back
empty, and exited 0. It now exits 1 before writing, unless the scraper
declares lists_files = False (RetroPie, whose files are inherited); RetroPie
in turn raises when its archive declares no package.
The cross-reference page read the label from the shape of cores:, so
RetroPie and RetroDECK, which name retroarch in a list, showed no
"(all libretro)". The label now comes from the resolved core set.
_stable_hashes retried through the hash cache, so both retries returned
the first reading and the file was read once before the manifest run
stopped. The retries now read the file again.
The gaps page called each platform's file a corrected list, a word the
exports page test already kept off that page. The test now reads the
text of every site generator.
Both are written as <platform>.json. --manifest-targets aimed at install/
replaced install/retroarch.json with a target table, and --manifest aimed
at install/targets/ did the reverse, both with exit 0. Each mode now stops
when the output directory holds the other kind and names the directory to
use.
gngeopi and neo-emu declare neogeo.zip loose and, apart, the ROMs they
open inside it. The archive pass took the copy holding them, the loose
entry resolved by name to another copy, and both verify --emulator and
the emulator pack reported the archive missing while the ZIP held it.
with_archive_members() gives the loose entry the members in verify, the
pack, slots and the gap report.
The index of other system spellings was rebuilt only when the page
table changed length, so a table of the same size kept the previous
links. The builder now returns its table and set_system_page_map
installs it with the index derived from it.
The Required packs and manifests carried every data tree, the FreeIntv
overlays included. A data_directories entry now accepts required: false,
and the platform, emulator and manifest walks leave such a tree out under
--required-only. FreeIntv reads its overlays only with the multi-screen
option and draws a placeholder keypad without one (src/libretro.c:294-296,
350-369).
The cache was truncated and dumped in place. A concurrent build read
half a file, took it for empty, and an offline run then wrote {} over
every recorded icon. The file is now replaced through a scratch file,
merged with what another run recorded meanwhile, and left alone when
nothing new was learned.
The MAME and FBNeo hash scrapers cached an empty parse for 24 hours and
reported OK. Both now raise before writing, and an empty cache is never
served as fresh.