fix: arbitrate slots on the strongest profile proof

This commit is contained in:
Abdessamad Derraz committed 2026-10-06 04:32:19 +02:00
1 parent a8415c33c3
commit 16f4c071fe
2 files changed
+46 -4

No files matched your search

+26 -4
View File
@@ -319,6 +319,15 @@ def pack_overrides(
return overrides
def _strongest(claims: list[Claim]) -> Claim:
"""The profile claim with the strongest proof; the first emulator breaks ties.
yaps2 pins its GameIndex.yaml by sha1 while armsx2 and lrps2 only match a
path: alphabetical order served armsx2's copy to all three.
"""
return max(claims, key=lambda claim: (_claim_rank(claim), -claims.index(claim)))
def arbitrate(conflict: Conflict, mode: str, addressee: str = "platform") -> Decision:
"""Decide a contested destination for the pack being built.
@@ -333,7 +342,7 @@ def arbitrate(conflict: Conflict, mode: str, addressee: str = "platform") -> Dec
addressee decides; the loss is reported rather than absorbed, because the
cause is an upstream declaration that needs fixing at its source.
"""
profile = conflict.profile_claims[0]
profile = _strongest(conflict.profile_claims)
if addressee == "emulator":
return Decision(conflict, profile, ADDRESSEE)
if not nativemode.reads_file_contents(mode):
@@ -345,11 +354,24 @@ def format_decision(decision: Decision) -> str:
"""One line naming the contested slot, the winner and the ground for it."""
conflict = decision.conflict
if decision.serves_both:
return (
f"{conflict.destination}: serve {decision.winner.local_path} "
f"({', '.join(conflict.emulators) or 'profile'}); the frontend only "
winner = decision.winner.local_path
served = sorted({
c.emulator for c in conflict.profile_claims
if c.emulator and c.local_path == winner
})
others = sorted({
c.emulator for c in conflict.profile_claims
if c.emulator and c.local_path != winner
})
line = (
f"{conflict.destination}: serve {winner} "
f"({', '.join(served) or 'profile'}); the frontend only "
"checks the path, so both are satisfied"
)
if others:
# One path holds one file: an emulator pinning another is not served.
line += f"; {', '.join(others)} expected another file"
return line
if decision.reason == ADDRESSEE:
return (
f"{conflict.destination}: serve {decision.winner.local_path}, "
+20
View File
@@ -555,5 +555,25 @@ class ManifestFollowsTheArbitration(unittest.TestCase):
self.assertEqual(by_dest[dest]["repo_path"], path)
class StrongestProfileClaimWins(unittest.TestCase):
"""yaps2 pins GameIndex.yaml by sha1; armsx2 matched a path only."""
def test_hash_proof_beats_path_proof(self):
from slots import Claim, Conflict, arbitrate, format_decision
def claim(emu, path, status):
return Claim("profile", "pcsx2/resources/GameIndex.yaml", "GameIndex.yaml",
emulator=emu, local_path=path, status=status)
conflict = Conflict(
"pcsx2/resources/GameIndex.yaml",
Claim("platform", "pcsx2/resources/GameIndex.yaml", "GameIndex.yaml",
local_path="p", status="path_exact"),
[claim("armsx2", "a", "path_exact"), claim("yaps2", "y", "sha1_exact")],
)
decision = arbitrate(conflict, "existence")
self.assertEqual(decision.winner.emulator, "yaps2")
self.assertIn("armsx2 expected another file", format_decision(decision))
if __name__ == "__main__":
unittest.main()