feat(ypp): the three eligibility bars, with dated thresholds (TASK 39·2)

YouTube moves the goalposts, so the app stops modelling them as its own truth and shows progress against
a threshold table that carries the date it was read.

- `Models/YppProgress.cs` — pure: three metrics, per-criterion met, and `MeetsPublishedRule`, which
  encodes the real shape of the rule (subscribers AND either view route). The drawer says "x of 3 criteria
  met — YouTube reviews eligibility" and spells out the either/or, so "2 of 3" can't be misread.
- `Services/YouTubeAnalyticsService.cs` — the two numbers only Analytics publishes: long-form watch hours
  (trailing 12 months, `creatorContentType==video`) and Shorts-feed views (90 days, `==shorts`). No rows
  means NO DATA, not zero.
- **Thresholds are data with a publication date** (`YppThresholds.AsOfUtc`, shown in the drawer) and a
  test fails once that reading is more than 180 days old. The 2027-02-01 doubling (4,000h → 8,000h,
  10M → 20M Shorts views) resolves from the date, not from an edit.
- **No Analytics scope → no invented numbers:** the drawer explains the missing consent instead of drawing
  two empty bars as zero. Money stays behind 3·20's "Show monetary metrics" toggle.
- Fan-funding tier (500 subs) demoted to a single muted line, since the three bars are the ad-revenue path.

Tests: `YppProgressTests` — either/or eligibility, subscribers alone insufficient, Shorts as a qualifying
route, bars clamp at 100%, the doubling date, the staleness guard, Analytics parsing (no rows = no data),
and compact value formatting. FAST gate: 567 passed, 0 failed, 0 warnings.
This commit is contained in:
2026-11-05 16:41:09 -08:00
parent 0fade9702b
commit a1d861608f
9 changed files with 418 additions and 50 deletions
+16 -1
View File
@@ -91,7 +91,22 @@ MyMistakes.
- 2FA: `https://myaccount.google.com/signinoptions/two-step-verification`
- AdSense: `https://support.google.com/youtube/answer/72851`
## Slice 2 (outline, queued)
## Slice 2 — ✅ SHIPPED 2026-11-05 (the three bars)
Shipped as designed: `yt-analytics.readonly` behind `IWatchMetricsSource`, three bars (subscribers,
long-form watch hours, Shorts views) against `YppThresholds.Tier2Now`, and one line —
"x of 3 criteria met — YouTube reviews eligibility." Notes recorded with it:
- **The view routes are either/or**, so the line is qualified in the drawer ("subscribers plus watch
hours, or subscribers plus Shorts views"); `YppProgress.MeetsPublishedRule` encodes that shape.
- **Thresholds carry a publication date** (`YppThresholds.AsOfUtc`, shown in the drawer) and a test
fails once that reading is older than 180 days — YouTube moves the bar and a stale bar must not look
current. The 2027-02-01 doubling is data, not a code change.
- **No analytics scope → no invented numbers**: the drawer says what is missing instead of drawing
empty bars as zero.
- Money stays behind the drawer's "Show monetary metrics" toggle (TASK 3·20).
## Slice 2 (original outline, for reference)
`yt-analytics.readonly` (+ re-consent) → `YouTubeAnalyticsService` behind `IChannelStatsProvider`
(`ai.md`): long-form `estimatedMinutesWatched` (12-mo), Shorts-feed views (90-d), then velocity /