From 184c822e6ef7e2341a9c0d8dbcc1f4bdbcc32ade Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Wed, 21 Oct 2026 05:33:50 +0200 Subject: [PATCH] fix: name the file each layer reads in slots.py Each content-checked line ended with a verdict on the platform's list, printed at pipeline step 3b. The line now says which emulators read the other file, and the closing count says the rest follow the platform's list. The module source carries no such verdict, a test reads it. --- scripts/slots.py | 21 +++++++++++---------- tests/test_slots.py | 3 +++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/scripts/slots.py b/scripts/slots.py index 7b27857f..6a5668bc 100644 --- a/scripts/slots.py +++ b/scripts/slots.py @@ -1,8 +1,8 @@ """Who decides what goes at a destination. A destination is a slot. Two layers can claim it: the platform YAML, scraped -from what the frontend declares and therefore able to carry an upstream error, -and the emulator profile, read from the emulator's own source. Until this +from what the frontend declares, which can pin another file than the emulator +reads, and the emulator profile, read from the emulator's own source. Until this module existed nothing compared the two: the builder resolves the platform entry first and drops the profile entry on a bare filename match, so a profile that names the right file for a slot could never win and never even be heard. @@ -614,8 +614,8 @@ def arbitrate( In ``existence`` mode the frontend only looks for a path, so the emulator's file satisfies both sides at once and there is nothing to trade away. In a content-checking mode the two answers cannot share one path, and the pack's - addressee decides. The loss is reported rather than absorbed, because the - cause is an upstream declaration that needs fixing at its source. + addressee decides. The loss is reported rather than absorbed: only the + platform's own list can change what its frontend accepts there. """ profile = _strongest(conflict.profile_claims) if addressee == "emulator": @@ -659,10 +659,11 @@ def format_decision(decision: Decision) -> str: f"{conflict.destination}: serve {decision.winner.local_path}, " "the pack answers to the emulator" ) + expected = ", ".join(conflict.emulators) or "the profile" return ( f"{conflict.destination}: keep {decision.winner.local_path}, the frontend " f"verifies content and would reject " - f"{conflict.profile_claims[0].local_path}; upstream declaration is wrong" + f"{conflict.profile_claims[0].local_path}, the file {expected} reads" ) @@ -692,8 +693,8 @@ def find_collisions( One path holds one file, so whichever declaration the builder reaches first decides in silence. Declaring an archive several times to name its inner ROMs is the documented zipped_file pattern and resolves to one - archive. Two declarations landing on two files is a contradiction the - upstream list carries. RetroDECK aims a PC-88 disk subsystem ROM and a + archive. Two declarations landing on two files name two files for one + path. RetroDECK aims a PC-88 disk subsystem ROM and a CoCo disk ROM at one bios/disk.rom. """ fold = bool(config.get("case_insensitive_fs", False)) @@ -1020,13 +1021,13 @@ def main() -> int: doubled = sum(len(c) for c in collided.values()) print( f"\n{total} contested destinations. {fixable} the pack settles on " - f"its own, {total - fixable} rest on an upstream declaration. " + f"its own, {total - fixable} follow the platform's list. " f"{doubled} destinations declared twice." ) if not args.strict and (total or doubled): print( - "Reported, not failed: the remainder needs the upstream list " - "corrected, which no build can do. Use --strict to gate on them." + "Reported, not failed: the remainder changes when the platform's " + "list does, which no build can do. Use --strict to gate on them." ) return 1 if ((found or collided) and args.strict) else 0 diff --git a/tests/test_slots.py b/tests/test_slots.py index d5444995..31f2ee58 100644 --- a/tests/test_slots.py +++ b/tests/test_slots.py @@ -414,6 +414,9 @@ class TestArbitration(unittest.TestCase): def test_the_reported_line_gives_the_ground_for_the_decision(self): kept = slots.format_decision(slots.arbitrate(self._conflict(), "md5")) self.assertIn("verifies content", kept) + # A fact about each layer, no verdict on the platform's list. + self.assertIn("the file dolphin reads", kept) + self.assertNotIn("wrong", slots.__loader__.get_source("slots").lower()) served = slots.format_decision(slots.arbitrate(self._conflict(), "existence")) self.assertIn("both are satisfied", served)