From 0835da8a91780beedb9651a26cadfc72a2d1fcaa Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Sun, 13 Sep 2026 10:56:18 -0400 Subject: [PATCH] fix: give the roster's column zip an explicit strict=False (387 D1, B905) ef91fcf failed ruff's B905 lane on one zip(labels, cells) without strict=. Tests, integration and the frontend were already green on that SHA. strict=False is the deliberate side, not the quiet one. strict=True raises a bare ValueError - not SubscribeStarDriftError - and would fail the whole roster sync over a column mismatch in `details`, which nothing reads yet. That would take down reconciliation and the gated-post reasons over a cosmetic markup change, while creator identity (id, slug) never depended on the columns at all. But a shifted column would mislabel details silently (a price filed under "discord"), so a count mismatch now logs a canary warning, mirroring the feed parser's existing parse canary: diagnosable from the worker log, never fatal. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01SHQB1YukL3VyvMK8rcbmV9 --- backend/app/services/subscribestar_client.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/backend/app/services/subscribestar_client.py b/backend/app/services/subscribestar_client.py index 0a1751b..e51e026 100644 --- a/backend/app/services/subscribestar_client.py +++ b/backend/app/services/subscribestar_client.py @@ -396,6 +396,16 @@ def _roster_rows(table: str, identifier: str, base: str) -> list[Membership]: name = _ROSTER_NAME_RE.search(row) slug = unescape(href.group(1)) cells = _ROSTER_CELL_RE.findall(row) + if len(cells) != len(labels): + # Canary, not a refusal. Identity above does not depend on columns, + # so a shifted column must not fail the whole roster — but it would + # silently mislabel `details` (a price filed under "discord"), so + # say so in the worker log where it is diagnosable. + log.warning( + "SubscribeStar roster %r: %d cells against %d headers — column " + "details may be mislabelled; markup likely changed (note #3989)", + identifier, len(cells), len(labels), + ) rows.append(Membership( campaign_id=user_id.group(1), display_name=(_cell_text(name.group(1)) if name else "") or None, @@ -420,7 +430,7 @@ def _roster_rows(table: str, identifier: str, base: str) -> list[Membership]: # its 's class at all. "columns": { label: _cell_text(cell) - for label, cell in zip(labels, cells) + for label, cell in zip(labels, cells, strict=False) if label not in _ROSTER_SKIP_COLUMNS }, },