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`),