fix(channels): address R14 deep-review findings

- Make the immediate-backfill guard SYMMETRIC: run_deep_backfill now also
  registers the channel it pages in _backfill_in_progress, so it and
  backfill_channel_now mutually exclude per-channel and never advance the
  same cursor concurrently (whichever started first). The interactive cap
  moves to a separate _immediate_running counter so the scheduler's channels
  don't false-trip it.
- Attribute the immediate backfill's spend: wrap it in
  quota.attribute(user_id, VIDEOS_BACKFILL_FULL) (route passes user.id), so
  it shows under the user's full-backfill action, not the OTHER/system bucket.
- Tests: the update_channel->background-task wiring + the demo guard
  (scheduled for a real user, NOT for demo); distinct N/S/L counts (2 short
  + 1 live) so a short/live swap fails; extracted channelDetailPollMs with a
  pure unit test of the poll-or-stop predicate.
- a11y: role="img" on the fully-synced charm so screen readers announce it.
This commit is contained in:
2026-08-15 07:11:50 +02:00
parent 2cc77757d9
commit 5720356191
7 changed files with 159 additions and 59 deletions
@@ -0,0 +1,24 @@
import { describe, it, expect } from "vitest";
import { channelDetailPollMs } from "./channelDetailPoll";
import { type ChannelDetail } from "./api";
// Only deep_requested + backfill_done drive the decision; cast a partial for the pure predicate.
const d = (over: Partial<ChannelDetail>): ChannelDetail => over as ChannelDetail;
describe("channelDetailPollMs", () => {
it("polls at 4s while THIS user's full-history backfill is running", () => {
expect(channelDetailPollMs(d({ deep_requested: true, backfill_done: false }))).toBe(4000);
});
it("stops (false) once the back-catalogue is fully in", () => {
expect(channelDetailPollMs(d({ deep_requested: true, backfill_done: true }))).toBe(false);
});
it("stops (false) when the user hasn't requested full history", () => {
expect(channelDetailPollMs(d({ deep_requested: false, backfill_done: false }))).toBe(false);
});
it("stops (false) before the detail has loaded", () => {
expect(channelDetailPollMs(undefined)).toBe(false);
});
});
+8
View File
@@ -0,0 +1,8 @@
import { type ChannelDetail } from "./api";
/** How often the channel page re-polls its detail (via useLiveQuery): every 4s while THIS user's
* full-history backfill is running (`deep_requested && !backfill_done`) so the stored count + the
* N/S/L breakdown climb on their own, and `false` (stop polling) once the back-catalogue is in or
* the user never requested it. Extracted so the poll-or-stop decision is unit-testable. */
export const channelDetailPollMs = (d: ChannelDetail | undefined): number | false =>
d?.deep_requested && !d.backfill_done ? 4000 : false;