From c781193ec99778c846aa05880a4e9c387791026b Mon Sep 17 00:00:00 2001 From: Aryan Singh Date: Wed, 2 Sep 2026 11:44:36 -0500 Subject: [PATCH] Count the peers actually tried, not the attempt counter A job carried over from before the candidate ladder has attempts=0 while carrying a real failure in `tried`, so the summary rendered as "tried 0 peers: 1x no bytes in 20m" -- a count of zero next to the evidence contradicting it. The live instance produced exactly that when the stall check finally cleared its long-stuck job. Counts distinct usernames in `tried` instead. The two agree for anything this version enqueued; the list is the one that is right in both cases. Co-Authored-By: Claude Opus 5 --- app.py | 13 ++++++++++--- tests/test_acquisition.py | 7 +++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app.py b/app.py index 82bbcea..02f4f31 100644 --- a/app.py +++ b/app.py @@ -895,13 +895,20 @@ def no_candidate_detail(seen: int, stats: "collections.Counter", def exhausted_detail(tried: list[dict], attempts: int) -> str: - """Summarise the whole ladder instead of overwriting it with the last error.""" + """Summarise the whole ladder instead of overwriting it with the last error. + + Counts `tried`, not `attempts`. They agree for anything this version + enqueued, but a row carried over from before the ladder existed has + attempts=0 with a real failure recorded against it, which rendered as the + nonsense "tried 0 peers: 1x no bytes in 20m". The list is the evidence. + """ if not tried: return "no candidate could be enqueued" + n_peers = len({t.get("username") for t in tried if t.get("username")}) or len(tried) reasons = collections.Counter(t["reason"].split(":")[0] for t in tried) - plural = "" if attempts == 1 else "s" + plural = "" if n_peers == 1 else "s" joined = ", ".join(f"{n}x {r}" for r, n in reasons.most_common(3)) - return f"tried {attempts} peer{plural}: {joined}" + return f"tried {n_peers} peer{plural}: {joined}" async def try_next_candidate(job_id: str, artist: str, title: str, diff --git a/tests/test_acquisition.py b/tests/test_acquisition.py index 6375225..d3b9ab1 100644 --- a/tests/test_acquisition.py +++ b/tests/test_acquisition.py @@ -195,6 +195,13 @@ def main() -> int: check("only fails once the ladder is exhausted", r["state"] == "failed", r["detail"]) check("failure names every attempt, not just the last", "tried 3 peers" in (r["detail"] or ""), r["detail"]) + # A row carried over from before the ladder has attempts=0 but a real + # failure recorded, which must not render as "tried 0 peers: 1x ...". + check("a migrated row counts its evidence, not its counter", + app.exhausted_detail( + [{"username": "u1", "reason": "no bytes in 20m"}], 0) + == "tried 1 peer: 1x no bytes in 20m", + app.exhausted_detail([{"username": "u1", "reason": "no bytes in 20m"}], 0)) print("\nattempt cap") fake2 = FakeSlskd(per_query={"Creep": [