From eb0dd91126fab7fa3e75386c4a5f9981218ff960 Mon Sep 17 00:00:00 2001 From: gramps Date: Mon, 28 Sep 2026 09:25:27 -0700 Subject: [PATCH] =?UTF-8?q?docs:=20OAuth=20verification=20is=20PARKED,=20n?= =?UTF-8?q?ot=20the=20next=20step=20=E2=80=94=20the=20map=20was=20lying?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 2026-09-27 entry sat under a "NEXT STEP" heading and framed the Google verification as the top priority. It was neither un-attempted nor un-blocked: the attempt had already failed on a Google-side bug and the creator had already ruled it unimportant for where we sit. A "next step" heading lends a dead end borrowed authority, and the next session would have opened the console instead of the editor. Records the ruling in the terms that change behaviour: attempted, died on Google's side, ruled unimportant, do not re-attempt. States the cost of tabling it honestly — an accepted 7-day refresh-token expiry while the consent screen sits in Testing, which is why the creator kept having to re-consent. The sensitive-vs-restricted classification was never read and is moot until a second real user or Store submission. The mechanism of the Google-side bug was never recorded and is left deliberately blank rather than reconstructed — a confident fiction in a memory map is the one sentence the next session would trust. The project number and console link stay in ai.md:233; they cost a hunt and are still true. The two code scope cleanups survive the tabling (neither needs verification to land): drop the redundant force-ssl scope, and move the uploads-playlist fetch off the user's session token. The channels?mine=true call on the same service legitimately needs the session — flagged so it is not "fixed" later. No code touched. Test state unchanged. --- HANDOFF.md | 40 ++++++++++++++++++++++++---------------- MyMistakes.md | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/HANDOFF.md b/HANDOFF.md index d533e4e..bfc0cd2 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -36,32 +36,40 @@ browser. **Coverage gap:** the *ordering* fix (defect 1) has no automated test hardcoded in `MainViewModel` and `ChangeAccountAsync` is private, so a real test would shell out to the browser. Wiring an auth seam into `MainViewModel` is a deliberate follow-up, not a bug-fix extra. -## ⏭ NEXT STEP (2026-09-27) — Google OAuth verification: open blocker, nothing recorded before +## 🗄 PARKED — Google OAuth verification: attempted, hit a Google-side bug, tabled by ruling + +**Creator ruling: unimportant for where we currently sit.** Verification is *not* a live blocker and +**must not be re-attempted, re-derived, or re-researched** without a new ruling. The 2026-09-27 entry +that preceded this one wrongly sat at `⏭ NEXT STEP` and framed it as the top priority — that was the +plan going in, not the outcome, and the outcome was never written down. Corrected 2026-09-28. + +**What happened:** the verification attempt was made and failed on a bug **on Google's side** — not +something we can fix, work around, or retry our way past. ⛔ **The exact mechanism was never recorded +and is deliberately left blank** rather than reconstructed from memory: a confidently wrong note here +is worse than none, because the next session would trust it. Do not go looking for the cause. **Owning account (creator-confirmed): `llamachile.shop@gmail.com` ("gramps").** Cloud project number -`198374416003` (from the ClientId). Open `https://console.cloud.google.com/apis/credentials?project=198374416003` -signed in as that account. Note the project picker defaults to the **"Recent"** tab — switch to -**"All"**. ⛔ Do not confuse this account with the YouTube channel account used in the app. +`198374416003` (from the ClientId) — banked in `ai.md:233`, **do not re-hunt it.** The one-hop console +link is `https://console.cloud.google.com/apis/credentials?project=198374416003` (picker defaults to +**"Recent"** — switch to **"All"**). ⛔ Not the same account as the YouTube channel signed into the app. -**Answer these two, and the whole verification path is known:** -1. OAuth consent screen **Publishing Status** — "Testing" or "In production"? If still *Testing*: - **100 test-user cap and refresh tokens expire every 7 days**, which is exactly why the creator kept - having to re-consent. Both limits vanish on going to production. -2. Is `https://www.googleapis.com/auth/youtube` flagged **sensitive** or **restricted**? Sensitive → - Google review only (free, self-service, weeks). Restricted → adds an **annual CASA security - assessment by a Google-approved third party** (real cost, recurring, "several weeks" per Google). - Google publishes the scope list but **deliberately does not publish this classification** — the - indicator lives only in the console, so it cannot be researched, only looked up. +**What tabling costs, stated honestly:** if the consent screen is still in **Testing**, the 100-user +cap and the **7-day refresh-token expiry** both stand — that expiry is why the creator kept having to +re-consent. Parking verification does not fix that; it accepts it for the solo-use phase. It becomes +real work the moment a *second real user* needs to sign in, or at Store submission (TASK 48). The +sensitive-vs-restricted classification was never read and is moot until then. -**Scope cleanups found (2026-09-27, not yet done — both trivial, both reduce verification surface):** +**These two survive the tabling** — neither needs verification to land, and both shrink the surface +if it ever comes back: - `Services/YouTubeAuthService.cs:18` requests `auth/youtube` **+ `auth/youtube.force-ssl`**. `force-ssl` is **redundant** — Google's liveChat docs list `auth/youtube` as an acceptable alternative, and it is the broader of the two ("Manage your YouTube account" ⊃ "see/edit/delete your videos, ratings, comments, captions"). Drop it: one fewer scope to justify, one fewer consent line. -- `Services/ChannelStatsService.cs:107` calls `playlistItems.list` on the creator's own uploads - playlist (`UU…`) — **public data, needs no OAuth scope at all** (API key suffices). It currently +- `Services/ChannelStatsService.cs:104,107` — `playlistItems.list` on the creator's own uploads + playlist (`UU…`) is **public data needing no OAuth scope at all** (API key suffices); it currently rides on the user's session token. Does not change the requested scope (`auth/youtube` is forced by `liveBroadcasts.*`/`liveStreams.list` regardless) but that fetch should not need user credentials. + The `channels?mine=true` call on the same service **does** need the session — do not "fix" it. - `auth/youtube` is **unavoidable**: no narrower scope covers live broadcast create/transition. ## ✅ DECIDED 2026-09-27 — pricing: `$10/mo` subscription, or `$400` lifetime, **no annual** diff --git a/MyMistakes.md b/MyMistakes.md index a7223ee..08327c2 100644 --- a/MyMistakes.md +++ b/MyMistakes.md @@ -1234,3 +1234,35 @@ parallel load — **it is not a signal that your change broke notifications.** B re-run it alone (`vstest --Tests:Info_Auto_Dismisses_And_Error_Sticks`); only treat it as yours if it fails in isolation too. See also the two other suite-level timing traps already recorded above (the one-tick pipe read, and blocking waits on the shared `RealApp` STA thread). + +### Writing the *plan* into the handoff instead of the *outcome* — the OAuth verification trap + +On 2026-09-27 I wrote a HANDOFF entry titled "record the Google Cloud project owner + the OAuth +verification blocker". What it actually contained was the two console questions I was about to go +answer, filed under `⏭ NEXT STEP` — which made the top of the file say *the next thing we do is +verify with Google*. The verification attempt had **already failed on a Google-side bug** and the +creator had **already ruled it unimportant for where we sit**. I knew the ruling; the map still said +the opposite, and the next session would have opened the console before opening the editor. + +**The failure is not that the fact is missing — it's that the file is optimistic by construction.** +A "next step" heading is a promise that this is un-attempted and un-blocked, so parking a dead end +under one is worse than omitting it: the heading lends the dead end borrowed authority it does not +deserve. When an attempt dies on someone else's bug, the handoff must say **attempted → died on their +side → ruled unimportant → do not re-attempt**, in those words, where the next session will read it. + +Two corollaries, both paid for here: + +- **Record the ruling, not just the discovery.** "Here is the project number" is cheap and I wrote + it. "Here is why we stopped, and what it cost us to stop" is the part that changes behaviour, and + it is the part I skipped. The real cost — an accepted 7-day refresh-token expiry while the consent + screen sits in Testing — was never written anywhere. +- **Leave the unknown blank, loudly.** I cannot now say what the Google-side bug *was*. Writing a + plausible mechanism would be fabrication, and it would be the most trusted sentence in the file, + because it is the one that explains everything else. `HANDOFF.md` now carries an explicit + "deliberately left blank, do not reconstruct it" marker. An honest gap beats a confident fiction + in a memory map; in a memory map especially, since the whole point is that the next session skips + the search. + +**Generalisation: a memory map entry that makes the reader's *next action* wrong is a bug, not an +omission.** When a fact is superseded by a ruling, the ruling wins the headline — the stale detail +belongs underneath it, labelled as what it is.