The home page and the stats export counted every file carrying a
provenance record, the provenance page and the README only the system
files. The site published 553 and 566 for the same quantity, one click
apart, and the export paired the wider count with composition.systems as
its denominator. common.count_catalog_matched is now the single source,
scoped to the systems bucket.
The FAQ had drifted from the profiles it describes: MAME pinned at 0.287
against 0.289 in mame.yml, Adler-32 attributed to Dolphin's IPL rather
than the DSP ROMs that carry known_hash_adler32, and the per-emulator
verbose report named as the only content check on an existence platform,
which skips the DISCREPANCY line the platform report raises itself.
Tests read both sides: no generator may count matches inline, and each
FAQ claim is checked against the profile or the script that owns it.
generate_emulator_page rendered the header, the metadata rows, the
platform block, every structured field and the file table in one body.
Four pieces come out - the scalar rows, the platform block, the
collapsible fields, and the predicate deciding whether the collection
holds a file - and the page reads as the sequence it always was.
Complexity 68 to 51, each new piece A or B rank.
Verified against the previous revision on frozen inputs: every generated
page identical.
Three groups leave generate_site.py: the write-and-sweep bookkeeping the
whole build shares, the pinned permalinks into cited sources, and the
small renderings every page uses. Each sits below what calls it, so the
page generators stay the only composite layer.
Verified against the previous revision on frozen inputs: 571 artefacts
identical, and the rendered site still resolves all local links across
511 pages.
common.py had grown to 1833 lines by accumulation. Six coherent pieces
move out - untrusted parsing, digests, archives, generated artefacts,
release assets, dump catalogues - and common.py re-exports them, so the
sixty existing import sites keep working and migrating them stays
optional.
The site build is now reproducible, which is what made the move
checkable. It deleted its generated directories first, so every page was
new and write_if_changed had no earlier version to compare against: a
deploy republished six hundred pages for the clock alone. Directories
are swept instead, a page is removed only once nothing produces it, and
the body pass compares against the body of the file on disk rather than
against the decorated page. Two consecutive builds on the same inputs
now produce identical bytes; before, 1034 files differed.
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.
generate_platform_page reached complexity 49, most of it in the block
rendering one collapsible section per system: each file shows a
different set of hashes, sizes and provenance depending on what the
platform declares. That block is now its own function and the caller
sits at 28. All 534 pages identical apart from timestamps.
generate_cross_reference rendered the same relation twice in one
function, once per platform and once per upstream, and reached
complexity 53. Each view is now its own function and the caller is a
pair of calls.
Verified by generating the site with both versions against the same
data: all 533 pages identical apart from timestamps, with a control run
of the same code twice to confirm the comparison had no drift in it.
generate_gap_analysis reached complexity 56. Its problem-files section
is the one that stands alone -- it reads platform_problems and nothing
else -- so it becomes its own function and the caller drops to 43.
The neighbouring sections were tried too and put back: they share
gap_report and the resolved core list, so pulling them out turns a
render pass into an argument-threading exercise for no gain. Verified
by generating the site with both versions against the same data: 529 of
530 pages identical, the odd one a wiki page another run had edited.
Links to this site get shared on Discord, Reddit and forums, where a
page with no Open Graph tags renders as a grey rectangle. The pages
already carry a per-page title and description, so a theme override
fills the tags from those; mkdocs-material would otherwise only emit
them through its social plugin, which pulls in Pillow and CairoSVG to
render a preview image these pages do not need.
generate_emulator_page carried a 228-line loop body rendering one file
row from thirty-odd optional fields, which put its complexity at 141,
more than twice the next function in the repository. The row renderer
and its badge strip are now their own functions and the page function
sits at 68.
_forge_sources went from 18 to 32 when it learned to pair each
repository with its own revision; the pairing is now its own function
and the caller is back under the threshold.
Verified by regenerating the site and diffing: all 505 pages are
identical apart from their generation timestamps.
A profile whose builds live in separate repositories keys source,
upstream and source_commit by build mode. The site flattened the URLs
but read a single scalar revision, so a libretro fork could be pinned
to the standalone commit, and binding the object form into SQLite
failed outright once ymir adopted it. URL and revision are now read as
pairs.
The site also published a sitemap nothing pointed at, so robots.txt is
generated alongside it.
The metadata behind the verifier, the pack builder and the site is now
served as static files: a versioned JSON API, CSV extracts, a SQLite
snapshot, and a catalog carrying a SHA-256 for each artifact.
The gaps dataset covers both layers behind a layer column. It previously
held one row, the single platform-verification anomaly, while the page
offering it as a download led with the emulator-level count.
source_ref renders as a permalink pinned to the revision the profile
cites. When a profile declares two repositories, a path that belongs to
neither by name is left as plain code: a citation without a link still
names the file and the lines, a link to the wrong repository does not.
The table filter, focus outlines and tap targets are progressive
enhancement; the pages work without them.
Add and reorder BIOS path entries in the site generator (BizHawk, EmuDeck, RetroPie, RomM). Update the add-platform pipeline steps and CI workflow notes. Document verification behavior changes: FirmwareDatabase index now includes sha256; RomM uses MD5 verification (verify.py checks MD5 only); BizHawk uses SHA1; severity label for GREEN adjusted to WARNING. Clarify troubleshooting/verify output semantics (UNTESTED and mismatch reporting), add profiling fields (core_classification option and adler32), fix several path and link typos (RetroDECK path, README/CONTRIBUTING links), and other small docs polishing.