docs: pre-GA posture — visibility lock rationale, branding-flash preview-only + escalation, screens/layers audit retired into gold pass

- ai.md: Private-only lock is deliberate test-phase policy (channel protection); DVR/VOD stay on
  as invisible review tapes; unlock is a TASK 36 item, never opportunistic.
- ai.md: free-tier flash escalates cadence (build-time curve knob); pre-GA it renders in preview
  only, never on output/recordings; TASK 36 flips it live.
- ai.md: 2026-08-22 screens/layers audit landmine closed as a going-gold checklist requirement.
- TASKS.md: TASK 9 item 6 ☐→❌ deliberate lock, cross-referenced.
This commit is contained in:
2026-09-01 15:32:16 -07:00
parent 8fa54d2de7
commit 7c6ec3e40a
2 changed files with 18 additions and 7 deletions
+1 -1
View File
@@ -679,7 +679,7 @@ the validator → persisted), compositor bar overlay (top/bottom + above-flash),
3. ☑ **Health monitoring** — poll `liveStreams.list` `healthStatus` + `configurationIssues[]`, surface banner only on warning/error (SHIPPED: `GetStreamHealthAsync(streamId)` 30s while live; pure `StreamHealthReporter.BannerFor` = report-by-exception; banner strip under the top bar, amber warning / dark-red error, via `HealthIssueBanner`/`HealthIssueBackground`; poll failures log-only; ONE integration test `GetStreamHealthAsync_Report_By_Exception_Banner_Only_On_Warning_Or_Error`) 3. ☑ **Health monitoring** — poll `liveStreams.list` `healthStatus` + `configurationIssues[]`, surface banner only on warning/error (SHIPPED: `GetStreamHealthAsync(streamId)` 30s while live; pure `StreamHealthReporter.BannerFor` = report-by-exception; banner strip under the top bar, amber warning / dark-red error, via `HealthIssueBanner`/`HealthIssueBackground`; poll failures log-only; ONE integration test `GetStreamHealthAsync_Report_By_Exception_Banner_Only_On_Warning_Or_Error`)
4. ☐ Live chat — poll `liveChat/messages`, render in right panel, support Super Chat + membership badges 4. ☐ Live chat — poll `liveChat/messages`, render in right panel, support Super Chat + membership badges
5. ☐ Error handling — the YouTube error codes: `errorStreamInactive`, `invalidTransition`, `redundantTransition`, `liveStreamDeletionNotAllowed`, `liveStreamModificationNotAllowed`, `liveBroadcastBindingNotAllowed` 5. ☐ Error handling — the YouTube error codes: `errorStreamInactive`, `invalidTransition`, `redundantTransition`, `liveStreamDeletionNotAllowed`, `liveStreamModificationNotAllowed`, `liveBroadcastBindingNotAllowed`
6. ☐ **Visibility picker** — remove temporary "always Private" enforcement (shipped as test-only; now unlocked for v1). User picks Private/Unlisted/Public from the go-live dialog. Trivial: remove the hardcoded override in `YouTubeStreamService.CreateBroadcast` (currently `privacyStatus = "private"` regardless of dialog selection) 6. ❌ **Visibility picker** — DELIBERATE TEST-PHASE LOCK (creator decision 2026-09-01), not open work: "always Private" stays during multi-month real-world testing so breakage VODs never clutter the channel. `recordFromStart`/DVR stay on — Private VODs are invisible review tapes; bulk-delete pre-GA. Unlock = TASK 36 gold-pass item (remove the override in `YouTubeStreamService.CreateBroadcast`, wire the dialog selection). See `ai.md` → YouTube Live API constraints.
7. ☐ **Full broadcast form** — expose all YouTube API-supported fields in the go-live dialog. Core tab: title, description, visibility, made-for-kids, schedule (start + optional end). Advanced tab (expandable, sane defaults): latency (Normal/Low/Ultra-Low), DVR, embed, record-from-start, projection (rectangular/360°), closed captions, auto-start, auto-stop, monitor stream, region restrictions. Monetization via `liveBroadcasts.update` (insert-only on that resource) — separate step after broadcast creation. Remove unsupported `categoryId` (not a `liveBroadcast` field, silently ignored) 7. ☐ **Full broadcast form** — expose all YouTube API-supported fields in the go-live dialog. Core tab: title, description, visibility, made-for-kids, schedule (start + optional end). Advanced tab (expandable, sane defaults): latency (Normal/Low/Ultra-Low), DVR, embed, record-from-start, projection (rectangular/360°), closed captions, auto-start, auto-stop, monitor stream, region restrictions. Monetization via `liveBroadcasts.update` (insert-only on that resource) — separate step after broadcast creation. Remove unsupported `categoryId` (not a `liveBroadcast` field, silently ignored)
### Design decisions (v3) ### Design decisions (v3)
+17 -6
View File
@@ -180,9 +180,9 @@ C# / WPF (.NET 8) following MVVM:
- Resolution tiers: 1080p60@8 (default) → 1080p30@8 → 720p60@6 → 720p30@6 → **Vertical 1080p60@8 (9:16, 1080×1920)**. The composition master frame is **always 1920×1080** — a tier is an output rect + target resolution over that master, so source geometry is never rewritten (no rounding drift). 16:9 tiers use the full frame; the vertical tier uses a centered **607×1080** window and the preview dims the cropped side strips at 55% black with an accent outline (semi-crop — the cut area stays visible). A resolution badge in the preview corner shows the active tier. Bottom bar: gear icon far left, stream stats (bitrate/FPS/dropped/duration) centered under preview, resolution dropdown far right. A **tooltip** explains finding upload bandwidth — an in-app speed test was deliberately dropped (unreliable). The future encoder crops the master to the rect and scales to the tier's Width×Height - Resolution tiers: 1080p60@8 (default) → 1080p30@8 → 720p60@6 → 720p30@6 → **Vertical 1080p60@8 (9:16, 1080×1920)**. The composition master frame is **always 1920×1080** — a tier is an output rect + target resolution over that master, so source geometry is never rewritten (no rounding drift). 16:9 tiers use the full frame; the vertical tier uses a centered **607×1080** window and the preview dims the cropped side strips at 55% black with an accent outline (semi-crop — the cut area stays visible). A resolution badge in the preview corner shows the active tier. Bottom bar: gear icon far left, stream stats (bitrate/FPS/dropped/duration) centered under preview, resolution dropdown far right. A **tooltip** explains finding upload bandwidth — an in-app speed test was deliberately dropped (unreliable). The future encoder crops the master to the rect and scales to the tier's Width×Height
- Crash diagnosis: `AppLog` writes startup checkpoints to `%APPDATA%\ytLlive\startup.log`; `App.xaml.cs` logs `DispatcherUnhandledException`/`AppDomain.UnhandledException`. When WPF won't run from WSL, this log is how you find the failure (it caught the `MenuItemRole.Separator` XAML crash and the ComboBox SelectionBoxItem bug) - Crash diagnosis: `AppLog` writes startup checkpoints to `%APPDATA%\ytLlive\startup.log`; `App.xaml.cs` logs `DispatcherUnhandledException`/`AppDomain.UnhandledException`. When WPF won't run from WSL, this log is how you find the failure (it caught the `MenuItemRole.Separator` XAML crash and the ComboBox SelectionBoxItem bug)
### ⚠️ Known issue: screens/layers settings audit needed (2026-08-22) ### ⚠️ Known issue: screens/layers settings audit — RETIRED INTO THE GOLD PASS (2026-08-22, dispositioned 2026-09-01)
The AI hallucinated through multiple commits tonight on background/scene property placement, repeatedly misreading the spec and introducing bugs. Specifically: moved properties to the wrong superclass level, used `new` instead of `override` breaking WPF bindings, removed working code, and added UI elements that weren't spec'd. **All screens/layers settings need a fine-tooth-comb audit** — every screen's Background layer properties, pill toggle persistence, context menu visibility, and the "Add Layer" menu items. Who knows what else got fucked up. Trust nothing the AI touched tonight without manual verification on a live build. The AI hallucinated through multiple commits that night on background/scene property placement, repeatedly misreading the spec and introducing bugs. Specifically: moved properties to the wrong superclass level, used `new` instead of `override` breaking WPF bindings, removed working code, and added UI elements that weren't spec'd. The audit was never run since. The creator ruled (2026-09-01): it is a **going-gold requirement**, not session debt — the fine-tooth-comb pass (every screen's Background layer properties, pill toggle persistence, context menu visibility, "Add Layer"/"(+)" menu items) is a named checklist item under **TASK 36 (gold pass)** and this landmine is closed here.
### Current limitations / TODOs ### Current limitations / TODOs
@@ -851,6 +851,14 @@ crooked.
an intermittent full-frame flash can't be cropped and is impractical to edit around on a live feed. an intermittent full-frame flash can't be cropped and is impractical to edit around on a live feed.
**The flash is also the free tier's billboard** — every free stream advertises LlamaCasty to its **The flash is also the free tier's billboard** — every free stream advertises LlamaCasty to its
own viewers; the free tier is distribution, not compromise. own viewers; the free tier is distribution, not compromise.
**Escalation model (2026-09-01, creator decision):** the cadence is *obnoxiously* self-promoting —
intervals shorten with use, starting at the 300s cadence and creeping toward a floor (the exact
curve is a build-time design knob). License activation still flips exactly one bit: `IsPremium` →
flash off. Nothing else changes between free and paid, ever.
**Pre-GA posture:** while the app is unreleased the flash renders **in the preview only** and is
never composited onto the live output or local recording — test VODs stay clean (same channel-
protection stance as the visibility lock), and creators can be shown what free looks like without
it ever touching a real broadcast. Flipping the flash live-on is a **TASK 36** unlock item.
- **Paid (annual subscription):** branding flash removed (flips `BrandFlashEnabled` off). That's it. - **Paid (annual subscription):** branding flash removed (flips `BrandFlashEnabled` off). That's it.
No feature gating. Alerts, social bar slots, voice filters, TRAX, recording — everything is free. No feature gating. Alerts, social bar slots, voice filters, TRAX, recording — everything is free.
- **Pricing:** early-access founders rate **$49.99/yr** → **$99/yr list at GA** (v1). **Grandfathering: - **Pricing:** early-access founders rate **$49.99/yr** → **$99/yr list at GA** (v1). **Grandfathering:
@@ -925,10 +933,13 @@ These are the hard facts behind every decision. Full list in `TASKS.md`.
`enableAutoStart=true`, `enableAutoStop=true`, `enableMonitorStream=false`, `enableAutoStart=true`, `enableAutoStop=true`, `enableMonitorStream=false`,
`selfDeclaredMadeForKids=false`, `latencyPreference=low`. The encoder starting brings YouTube live. `selfDeclaredMadeForKids=false`, `latencyPreference=low`. The encoder starting brings YouTube live.
`enableMonitorStream=false` is what lets us skip the testing stage. `enableMonitorStream=false` is what lets us skip the testing stage.
- **Visibility picker (TASK 9 item 6)** — the "always Private" enforcement was temporary for testing - **Visibility picker (TASK 9 item 6) — DELIBERATE TEST-PHASE LOCK, not drift (2026-09-01).**
during development ("always Private until v1"). For v1, the go-live dialog exposes Private/Unlisted/Public The "always Private" enforcement in `YouTubeStreamService.CreateBroadcast` stays while the creator
and `YouTubeStreamService.CreateBroadcast` uses the dialog's selection (not a hardcoded override). runs multi-month real-world testing: non-private test streams would clutter the channel with VODs
The PRIVATE badge in the top bar still shows when the stream is actually private. highlighting where the product breaks. `recordFromStart`/DVR stay ON — Private VODs are invisible
to subscribers and serve as post-mortem review tapes; bulk-delete pre-GA. The unlock is a
deliberate final-pass item in **TASK 36 (gold pass)**, wired with the dialog selection — never
opportunistic. The PRIVATE badge keeps showing when the stream is actually private.
- **Full broadcast form (TASK 9 item 7)** — the go-live dialog exposes all YouTube API-supported fields. - **Full broadcast form (TASK 9 item 7)** — the go-live dialog exposes all YouTube API-supported fields.
**Core tab** (always visible): title, description, visibility (Private/Unlisted/Public), made-for-kids, **Core tab** (always visible): title, description, visibility (Private/Unlisted/Public), made-for-kids,
schedule (start + optional end datetime). **Advanced tab** (expandable, sane defaults): latency schedule (start + optional end datetime). **Advanced tab** (expandable, sane defaults): latency