From 0fb2f9c6c35937f68bc458ae0cf8ac751c7c2b46 Mon Sep 17 00:00:00 2001 From: gramps Date: Sun, 4 Oct 2026 14:58:55 -0700 Subject: [PATCH] docs: Go Live IS private (verified), and v1 is where that guarantee ends MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The creator's standing doubt: "I wasn't sure that the go live video would be private." Answered by tracing every path that can touch privacy, not by reassuring: CreateBroadcast -> privacyStatus = "private" hardcoded GoLiveViewModel -> Visibility => "Private" hardcoded BeginTestStream -> StreamVisibility = "Private" hardcoded UpdateBroadcast -> the only code that can change it — NO CALLER UpdateBroadcast's sole caller in the repo is BroadcastPullOutTests.cs:120. It is dead code. So there is no path today by which a broadcast this app creates becomes non-private, and the "do not populate my channel with videos showing the app at its worst" concern is fully met. Verified, not assumed. Which makes the safety accidental rather than structural, and that is the part worth writing down. LiveBroadcastFormViewModel still offers VisibilityOptions {private, unlisted, public}, and UpdateBroadcast would honour it. So the v1 visibility option is precisely the change that first wires that method up — v1 is the moment private stops being guaranteed and becomes a user-selectable default. Intended at v1, ruinous before, and "it's dead code, let me call it" is not a valid reason to do it early. Also recorded, having been absent from the map entirely: the TEST button is hidden at v1. Its real job is being the guaranteed-private path (hence the hardcoded Private at BeginTestStream:105), which the existing "dry-run" comment on ShowTestButton does not capture — that framing is now known to be incomplete, and must not be used to re-derive why the button persists. Corrects a fact I got wrong one commit ago: the method is UpdateBroadcast, not "UpdateBroadcastMetadataAsync", and I described it as an available retrofit path when it is in fact unreachable in production. Open, deliberately, for v1 rather than now: whether a dry run is still wanted on its own merits once Test is hidden. --- TASKS.md | 19 +++++++++++++++++-- ai.md | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/TASKS.md b/TASKS.md index 21e38d9..7a9a88f 100644 --- a/TASKS.md +++ b/TASKS.md @@ -307,11 +307,26 @@ second encoder path needing a "redirect". Record+simulcast is one ffmpeg with tw restated 2026-10-04).** `privacyStatus = "private"` hardcoded in `YouTubeStreamService.CreateBroadcast` is deliberate and correct for the whole pre-v1 test phase — it is the reason test VODs of a still-breaking product stay off the channel. At v1 it - becomes a user-selectable visibility option in the Go-Live dialog, wired to the same - `UpdateBroadcastMetadataAsync` path. **Before v1, do not touch it and do not raise it as a + becomes a user-selectable visibility option in the Go-Live dialog. **Before v1, do not touch it and do not raise it as a question, observation or suggestion — it has been asked and answered repeatedly.** Full rationale and the retired "can we delete the test VODs?" question: `ai.md` → YouTube Live API design constraints. + - **Verified 2026-10-04: a Go Live video IS private.** Three independent hardcodes + (`CreateBroadcast` → `"private"`, `GoLiveViewModel.Visibility => "Private"`, + `BeginTestStream` → `StreamVisibility = "Private"`) and the one method that could change it, + `YouTubeStreamService.UpdateBroadcast`, has **no production caller** — only + `BroadcastPullOutTests.cs:120`. No path exists today by which an app-created broadcast becomes + non-private. + - **Two coupled v1 items — both are the deliberate end of that guarantee:** + 1. **Hide the TEST button.** It exists as the guaranteed-private path; once visibility is + selectable, a separate safe button is redundant. One-line change — + `ShowTestButton => ShowStreamActions && IsConnected` is a computed predicate, not a setting. + 2. **This is the unit that first wires `UpdateBroadcast` into production.** It is the only code + that can change a broadcast's privacy, and it is currently dead. Wiring it is exactly when + private stops being guaranteed and becomes a user-selectable default. Correct at v1, + ruinous before — and "it's dead code, let me call it" is not a reason to do it early. + - A **dry run** may still be worth having at v1 on its own merits; that is a separate question + from the privacy mechanism and is **not** decided by the above. - **Kill the dev multi-instance affordance.** `Helpers/InstanceProfile.cs` is already entirely `#if DEBUG`, so a Release build ships as `DataRoot => DefaultRoot` / `WebViewDataFolder => null`. diff --git a/ai.md b/ai.md index 19b5d99..6d0fe7e 100644 --- a/ai.md +++ b/ai.md @@ -1736,9 +1736,50 @@ These are the hard facts behind every decision. Full list in `TASKS.md`. `videos.delete` accepts `youtube.force-ssl` which we already hold, private is a visibility setting not a deletion lock, and anyway a broadcast that never reaches `live` never becomes a VOD at all). - - **`CreateBroadcast:112` is not the unlock** — `UpdateBroadcastMetadataAsync` can already set - visibility post-hoc, but that is the retrofit path, not the product change. The product change - is the dialog option at v1. + - **`UpdateBroadcast` is NOT the unlock — and it is also currently DEAD CODE. (Fact corrected + 2026-10-04: an earlier version of this note said `CreateBroadcast:112` / + `UpdateBroadcastMetadataAsync`, both wrong.)** The method that can change a live broadcast's + privacy is `YouTubeStreamService.UpdateBroadcast(broadcastId, meta)`, which sends + `privacyStatus = meta.Visibility ?? "private"`. **It has no production caller** — the only + caller in the repo is `BroadcastPullOutTests.cs:120`. So `CreateBroadcast` hardcoding private + is not merely backed up by convention, it is the *only* thing enforcing it. + - **⇒ Is a Go Live video private? YES — verified 2026-10-04, and this answers a standing + creator doubt.** Three independent hardcodes (`CreateBroadcast` → `"private"`; + `GoLiveViewModel.Visibility => "Private"`; `BeginTestStream` → `StreamVisibility = "Private"`) + plus one unreachable method. There is no path by which a broadcast this app creates can become + non-private today. The creator's actual concern — "do not populate my channel with videos + showing the app at its worst" — is fully met. + - **⛔ THE LANDMINE, and the reason the above matters:** `LiveBroadcastFormViewModel` still + exposes `VisibilityOptions = {private, unlisted, public}` and `UpdateBroadcast` would happily + honour it. **The v1 visibility option is the first thing that will ever wire `UpdateBroadcast` + up — so v1 is exactly when this guarantee ends, by the creator's own choice.** When that + happens, private stops being guaranteed and becomes a default the user can change. That is + intended at v1; it is a disaster before v1. Do not wire `UpdateBroadcast` into any + production path before v1, and do not "fix" the dead-code smell by calling it. +- **⛔ The TEST button is HIDDEN at v1 — it exists only because of the lock above (creator + ruling 2026-10-04).** This was **absent from the map entirely** until that date, which is how the + dry-run framing below came to be the only recorded one. Both are true, and the second is the + load-bearing one: + - **Its real job is being the guaranteed-private path.** `BeginTestStream` hard-sets + `StreamVisibility = "Private"` (`MainViewModel.Streaming.Operations.cs:105`), so Test can never + publish. That is *why* it exists for the whole pre-v1 phase — a dry run you cannot leak. + - **At v1 it is redundant and gets hidden**, because once visibility is a real dialog option the + user picks Private themselves and a separate "safe" button no longer earns its place. Hiding it + is part of the v1 visibility work, not a separate cleanup. + - **The pre-existing "dry run" framing is now known to be incomplete** (`ShowTestButton`'s own + comment calls it "a dry-run of the livestream"). Do not read that comment as the reason the + button must persist, and do not re-derive the button's purpose from it. If a dry run is still + wanted at v1 it must be re-justified on its own merits — that question is **open, and is for + v1, not now.** + - **It is a computed predicate, not a setting** — `ShowTestButton => ShowStreamActions && + IsConnected` (`MainViewModel.Streaming.cs:182`), feeding both `CanStartTest` and the XAML + visibility. So the v1 hide is a one-line predicate change, not a settings migration. + - **Not a v1 blocker:** the Test route is the *same* pipeline as Go Live — + `BeginTestStream` → `StartStreamingSession(alsoRecord: false)` → `PrepareAndStartLiveAsync` — + so Test and Go Live create identical broadcasts (`boundStreamId`, `enableAutoStart`). Test + differs only in never recording (`alsoRecord: false`) and forcing Private. **This is why Test + is the correct way to run any diagnostic stream**, and why a Test result is evidence about Go + Live. - **Full broadcast form (TASK 9 item 7) — RESCOPED 2026-09-01** — the core editable fields ship (since 2026-08-24) as the always-visible **Text drawer**: title, description, tags, visibility, made-for-kids, live-editable; scheduling ships as **TASK 34** (drawer ☑ Scheduled + adoption at Start). The old **Advanced tab is permanently out of product** (the 10% margin — see TASKS.md → "Out of product"): latency locked `low`, DVR/record-from-start locked on, embed/projection/CC/region fixed at sane defaults, invisible — every exposed field is a support ticket. `categoryId` is removed from `CreateBroadcast` (not a `liveBroadcast` field, silently ignored). Monetization enablement (if ever needed) rides the reward-events chain via `liveBroadcasts.update`, not a form field. Go-live order (TASK 9, shipped 2026-08-16): `BeginGoLive` → `PrepareAndStartLiveAsync` — ensure the reusable stream (`GetOrCreateReusableStreamAsync`, cache it), create the broadcast bound to it (`CreateBroadcast(..., stream.Id)` → `boundStreamId`),