docs(queue): six veggies, then the new work; scheduling and publishing retired

Creator ruling 2026-11-09: finish the queue before the new features, gold pass always last.

**Retired (platform's surface, not ours):**
- 34 scheduled streams — YouTube owns scheduling *and* the reminder that makes it worth anything.
- 50 publish recording — Studio owns upload (resumable, quota-aware, explains rejections), and an
  in-app uploader would widen the OAuth consent to whole-channel video management for one convenience.
  The app adds "Show in folder" instead.

**New tasks:** 65 replay clips (the creator-side buffer YouTube does not give you), 66 timed ticker
messages, 67 per-event alert videos, 68 edit-while-live + first-run On-Air.

**TASK 65 spec** is the interesting one: 15 s is a clip (10/15/20, default 15); the ring holds encrypted
*encoded* fragments in RAM — raw 1080p frames would be ~7.4 GB for 15 s — so there is no steady disk
write and no crash debris; F10 saves; the replay layer plays once then hides in Live and loops the last
1–3 elsewhere; silent by default; three kept; the layer is excluded from its own clip (hall of mirrors).

**TASK 14 gains Branch 3:** mic input gain ahead of the gate (table boom mics fall under it), webcam
blank hotkey, opacity/geometry parity with a collapsed Geometry disclosure, Show-in-folder.

**Decisions recorded:** the replay-buffer reversal (it was retired in 2022-era reasoning as "different
product"; the case for it won), the platform-surface rule, and voice triggering deferred — always-
listening in a streaming app hears the game, so the content is its own false-positive source.

Queue refactored to 56 lines by archiving completed rows and the superseded surge plan; HANDOFF at 40.
This commit is contained in:
2026-11-09 14:27:07 -08:00
parent 42228c7aab
commit ec6a5ca466
11 changed files with 254 additions and 97 deletions
+6 -6
View File
@@ -1,12 +1,11 @@
# HANDOFF — 2026-11-09 (TASK 33 policy; vertical checks passed) # HANDOFF — 2026-11-09 (TASK 33 done; queue re-ordered for 1.0)
## State ## State
- `main`. TASK 21 (video layer + soundtrack), TASK 64 (green/blue screen) and TASK 32·4 (fragmented MP4) - `main`. TASK 21 (video layer + soundtrack), TASK 64 (green/blue screen) and TASK 32·4 (fragmented MP4)
are complete; TASK 49 is retired (YouTube owns chat moderation — we just don't paint tombstones). are complete; TASK 49 is retired (YouTube owns chat moderation — we just don't paint tombstones).
- **Starting art**: `Assets/starting-background.jpg` is the corrected onboarding screen (earlier - **Starting art**: `Assets/starting-background.jpg` is the corrected onboarding screen; bundled art is
images in `Assets/archive/`). Bundled art is hash-keyed into the layout DB, so hash-keyed into the layout DB, so `StampDefaultBackgroundAsset` compares bytes and re-stamps.
`StampDefaultBackgroundAsset` compares bytes and re-stamps on a mismatch — no version bump.
- Left panel alert stack shipped: Steam licence indicator pinned at row 1, event rows LIFO, click → - Left panel alert stack shipped: Steam licence indicator pinned at row 1, event rows LIFO, click →
About or Logs with the event's lines painted. Detail: `docs/ai/ui-surface.md`. About or Logs with the event's lines painted. Detail: `docs/ai/ui-surface.md`.
- Site: demo video (silent loop) is the hero; Starting screen is in the scenes section. - Site: demo video (silent loop) is the hero; Starting screen is in the scenes section.
@@ -17,8 +16,9 @@
## Next action ## Next action
- **App queue starts at 34** (scheduled streams; cite its research flags first), then 35/37/40/50/55, - **Queue re-ordered (creator ruling):** 35 → 37 → 40 → 55 → Move → 9·5 → 14 slices → 65 (clips) →
Move transitions, 9·5. 66 → 67 → 68 → 36. TASK 34 (scheduling) and 50 (publishing) are **retired** — platform's surface.
- **New:** 65 replay clips, 66 timed ticker, 67 per-event alert videos, 68 edit-while-live.
- Site repo commits are local (`llgit.llamachile.tube`); deploy is `./deploy.sh`, not a push. - Site repo commits are local (`llgit.llamachile.tube`); deploy is `./deploy.sh`, not a push.
- Do not push the app absent a checkpoint request. - Do not push the app absent a checkpoint request.
- **Steam is a wait, not a task:** identity verification pending, tax review in Valve's queue (up to 30 - **Steam is a wait, not a task:** identity verification pending, tax review in Valve's queue (up to 30
+2 -1
View File
@@ -13,7 +13,8 @@ kept locally, and what is never collected.
| Your layout: scenes, sources, assets, settings | `%APPDATA%\ytLlive\ytLlive.db` | So the app looks the same next time | | Your layout: scenes, sources, assets, settings | `%APPDATA%\ytLlive\ytLlive.db` | So the app looks the same next time |
| YouTube sign-in token | `%APPDATA%\ytLlive\ytLlive.auth`, encrypted with Windows DPAPI (CurrentUser) | So you stay signed in; only your Windows account can decrypt it | | YouTube sign-in token | `%APPDATA%\ytLlive\ytLlive.auth`, encrypted with Windows DPAPI (CurrentUser) | So you stay signed in; only your Windows account can decrypt it |
| Diagnostic log | `%APPDATA%\ytLlive\startup.log`, capped at 5 MB | To diagnose crashes you report | | Diagnostic log | `%APPDATA%\ytLlive\startup.log`, capped at 5 MB | To diagnose crashes you report |
| Recordings | the folder you choose | Your files; the app never uploads them unless you press Publish | | Recordings | the folder you choose | Your files; the app never uploads them |
| Replay clips (if you use them) | `clips\` beside your recordings | Only the three most recent are kept; older ones are deleted automatically. Local only, never uploaded |
Nothing else is written to disk. There is no analytics service, no crash Nothing else is written to disk. There is no analytics service, no crash
reporter, no advertising SDK and no telemetry of any kind. reporter, no advertising SDK and no telemetry of any kind.
+36 -87
View File
@@ -1,106 +1,55 @@
# TASKS — the active queue # TASKS — the active queue
> One line per live item; the detail lives in `TASKS/task-*.md`. Done work is in > One line per live item; the detail lives in `TASKS/task-*.md`. Done work and the retired surge plan are
> [`TASKS/archive.md`](TASKS/archive.md) (2026-10-29 snapshot of the old catalogue). Retired > in [`TASKS/archive.md`](TASKS/archive.md). Retired decisions are in
> decisions are in [`docs/ai/decisions.md`](docs/ai/decisions.md). > [`docs/ai/decisions.md`](docs/ai/decisions.md).
> Markers: ☐ queued · 🔶 in progress · ⏳ blocked · ⏸ deferred · ❌ retired. > Markers: ☐ queued · 🔶 in progress · ⏳ blocked · ⏸ deferred · ❌ retired.
## The surge — six days, all remaining coding (2026-11-02 → 11-07) ## Next up
**Every open coding row is assigned below.** No later phase, no "after launch" list. Target is **Creator ruling 2026-11-09 — finish the queue before the new features, gold pass always last.**
**6–8 units a day** (above the measured 3–5 baseline — that is the point of a surge). If a day
overruns, the smallest item moves to the next day; a launch blocker never moves.
**Rules, every unit, no exceptions:** one Good Dog test · clean build 0 warnings · per-class tests `35 → 37 → 40·A/C/D → 55·2–3 → Move → 9·5 → 14 (slices) → 65 → 66 → 67 → 68 → 36`
green · `./scripts/verify.sh` on the declared scope · commit with the doc fact it falsifies
corrected. No new scope mid-week; anything discovered goes on a list for day 6.
| Day | Units | Done when | The rule behind the two retirements below: **do not rebuild the platform's own surface.** Scheduling,
|---|---|---| publishing, moderation and channel management are YouTube's; the app owns capture → scene → sound →
| **1 · Mon** — licensing | Paid-app ownership check (`BIsSubscribedApp`) + Debug fake + app-wide `IsAppOwned` · greyed tier entries · About conversion copy + llamacasty.com link · current-launch Logs tab (replaces the separate system-message strip) · Debug-only `InternalsVisibleTo` | A locked tier opens About without changing the selected free tier or leaving the app; the site opens only from its link | output.
| **2 · Tue** — release blockers | Visibility flow (creator-selected at release; development/test stays Private) · `OutputProfile` entitlement: 30 fps free / 60 fps paid regardless of resolution · ~~`StreamConfig` defaults~~ retired (no production call sites) · TASK 53 (vertical resolution check ✅; remaining output contract open) · 9·5 webcam identity key · Move transitions (per-layer carry-across-scenes) | Visibility is selectable at creation and live-editable; the 30/60-fps gate is tested; remaining Day 2 items stay queued |
| **3 · Wed** — resilience | TASK 32: blip retry → measured grace → amber RECONNECTING → one-click Back on air → fragmented MP4 → ingest pre-flight · 33 bandwidth step-down | Killing the network mid-broadcast recovers without ending it |
| **4 · Thu** — content & media | ~~3·17 Text layer~~ ✅ · ~~21 media acquisition wiring~~ ✅ (picker, loop, lifecycle) · 49 chat profanity filter · 34 scheduled streams | Text layer renders/persists; media file plays, loops; scheduled stream created |
| **5 · Fri** — data & settings | 3·20 RewardEvent persistence + `superChatEvents.list` backfill · 39·2 YPP slice 2 · 37 defaults-vs-current split · 35 scene-linked audio · 40·A/C/D settings round · 55·2–3 disk hygiene | Session report reads persisted events; defaults can be reverted; temp clips cleaned |
| **6 · Sat** — finish & prove | 50 publish recording · preview undo (Ctrl+Z) · 14 creator feedback batch · 36 gold pass · full sweep: clean build, whole per-class suite, `publish.sh` artifact, EULA/privacy/notices in the artifact · commit + push | Gold pass list empty; artifact stages; suite green; pushed |
**Day 6 is the only polish day.** No polish passes before it — a surge dies on cosmetic passes taken
in the middle.
## Next up (after the surge slice)
**Start here:** TASK 33 (bandwidth step-down) — the last unbuilt piece of the resilience row, and the
only launch blocker left in code. Then TASK 34 (scheduled streams; its two research flags must be
cited first), then 35 / 37 / 40·A/C/D / 55·2–3. TASK 32 is complete, Back on air included.
## Queue ## Queue
> Every open coding row here is assigned to a surge day above. Rows still marked ☐ on
> 2026-11-07 are a failure of the plan, not a scheduling note.
| Task | Status | Scope | Detail | | Task | Status | Scope | Detail |
|---|---|---|---| |---|---|---|---|
| 53 | ✅ | Output-format contract — six size/FPS modes, vertical YouTube playback, and recordings visually accepted | [`task-53`](TASKS/task-53-output-formats.md) | | 35 | ☐ | **Scene-linked audio** — the scene remembers the room: BRB mutes the mic, Chat restores it, applied once at the live transition | [`task-35`](TASKS/task-35-scene-linked-audio.md) |
| — | ✅ | Output entitlement code: free at 30 fps / paid at 60 fps regardless of resolution; Steam App ID is still pending | [`commercial`](docs/ai/commercial.md) | | 37 | ☐ | **Defaults vs current layout split** — separate branded defaults from the live workspace; enables one-click revert | [`task-37`](TASKS/task-37-defaults-current-split.md) |
| 3·17 | ✅ | **Text layer** — content, font family/size/weight/style, colour, alignment, drag/resize, persisted (done 2026-11-04) | [`task-03`](TASKS/task-03-capture-pipeline.md) | | 40·A/C/D | ☐ | **Settings round** — active-camera picker; persist output resolution + surface transition defaults + confirm-before-End; accent consolidation | [`task-40`](TASKS/task-40-app-settings-round.md) |
| 21 | ✅ | Media source: video file added from the (+) menu decodes, composites, plays its soundtrack, loops, and stops with its last reference (done 2026-11-05) | [`task-21`](TASKS/task-21-media-source.md) | | 55·2–3 | ☐ | **Disk hygiene** — delete `%TEMP%` alert clips on close + sweep at startup; low-disk guard before REC (slice 1 shipped) | [`task-55`](TASKS/task-55-disk-hygiene.md) |
| 64 | ✅ | **Green/Blue Screen** — detect + key from the decode pipe, offer on add, Amount/Softness/Fringe, opt-in auto-apply, alert clips included, persisted (done 2026-11-05) | [`task-64`](TASKS/task-64-green-blue-screen.md) | | — | ☐ | **Move transition** — per-layer carry-across-scenes flag, per-scene placement, fixed z band | [`ui-surface`](docs/ai/ui-surface.md) |
| — | ☐ | Transitions **Move**: per-layer carry-across-scenes flag, per-scene placement, fixed z band | [`ui-surface`](docs/ai/ui-surface.md) | | 9·5 | ☐ | **Webcam identity key reconciliation** — one stable identity per camera across re-plugs | [`task-09`](TASKS/task-09-live-stream-management.md) |
| 55·2–3 | ☐ | Disk hygiene: delete `%TEMP%` alert clips on close + sweep at startup; low-disk guard before REC (slice 1 shipped) | [`task-55`](TASKS/task-55-disk-hygiene.md) | | 14 | 🔶 | **Creator feedback batch** — Branch 2 shipped; Branch 3 = mic input gain, webcam blank/standby, opacity+geometry consistency, Show-in-folder on stop | [`task-14`](TASKS/task-14-creator-feedback-batch.md) |
| 40·A/C/D | ☐ | App Settings: active-camera picker; persist output resolution + surface transition defaults + confirm-before-End; accent consolidation | [`task-40`](TASKS/task-40-app-settings-round.md) | | 65 | ☐ | **Replay clips** — 10/15/20 s (default 15), RAM ring of fragmented MP4, F10 to save; replay layer plays once then hides in Live and loops the last 1–3 elsewhere; silent by default; three kept | [`task-65`](TASKS/task-65-replay-clips.md) |
| 3·20 | ✅ | Session report — **derived from `superChatEvents.list` on demand, not persisted** (YouTube is the authority; no ledger). Per-currency totals, money off by default (done 2026-11-05) | [`task-03`](TASKS/task-03-capture-pipeline.md) | | 66 | ☐ | **Timed ticker messages** — "every N minutes show this", from a creator-written list | [`task-66`](TASKS/task-66-timed-ticker.md) |
| 32 | ✅ | Stream resilience: fragmented MP4, blip retry, poll-driven RECONNECTING (no countdown — YouTube's cutoff is not a constant), ingest pre-flight, one-click Back on air (`BackOnAirTests`) | [`task-32`](TASKS/task-32-stream-resilience.md) | | 67 | ☐ | **Per-event alert videos** — a clip per monetization event, falling back to the default | [`task-67`](TASKS/task-67-per-event-alert-videos.md) |
| 33 | 🔶 | Auto step-down — **`StepDownPolicy` + 12 unit tests done 2026-11-09** (two ladders, budgets, floors, cooldown, ambiguity, recovery behind a flag); policy, planner, VM watch, encoder relaunch and the toast are in (17 tests); remaining: shed our own capture cost before the fps step, and the session-report line | [`task-33`](TASKS/task-33-bandwidth-step-down.md) | | 68 | ☐ | **Editing while live** + first-run On-Air state matching the 1-2-3 screen | [`task-68`](TASKS/task-68-edit-while-live.md) |
| 34 | ☐ | Scheduled streams (Text-drawer version) | [`task-34`](TASKS/task-34-scheduled-streams.md) | | 36 | ☐ | **Gold pass** — the v1 gate: screens/layers audit (every control does something), native-Windows verification, branding-flash decision. Always the last unit | [`task-36`](TASKS/task-36-gold-pass.md) |
| 35 | ☐ | Scene-linked audio | [`task-35`](TASKS/task-35-scene-linked-audio.md) | | 34 | ❌ | **Scheduled streams — retired 2026-11-09.** YouTube owns scheduling *and* the reminder that makes it useful; the app neither creates nor displays scheduled broadcasts, and there is no value in a copy that can fall out of sync | [`decisions`](docs/ai/decisions.md) |
| 37 | ☐ | Defaults vs current layout split (build-id); enables one-click revert to defaults | [`task-37`](TASKS/task-37-defaults-current-split.md) | | 50 | ❌ | **Publish recording — retired 2026-11-09.** YouTube Studio owns upload (resumable, quota-aware, and it explains rejections). In-app publishing would also widen the OAuth consent to whole-channel video management. The recording lands in the creator's folder; the app adds "Show in folder" instead | [`decisions`](docs/ai/decisions.md) |
| — | ✅ | **Preview undo (Ctrl+Z / Ctrl+Shift+Z)** — done 2026-11-01: `Models/PreviewHistory` + `MainViewModel.Preview`, one step per gesture, gated on `EditingActive`, cleared on layout load. Property edits not yet in the stack | [`ui-surface`](docs/ai/ui-surface.md) | | 54 | 🔶 | Freemium: EULA + privacy shipped; one codebase for free Demo + paid app. **Blocked on Valve** — identity verification pending, tax review up to 30 business days | [`task-54`](TASKS/task-54-marcom-free-merch.md) |
| 39·2 | ✅ | YPP slice 2 — three eligibility bars (subs / watch hours / Shorts views) from Analytics, dated thresholds with a staleness test, "x of 3 criteria met" (done 2026-11-05) | [`task-39`](TASKS/task-39-ypp-journey-tracker.md) |
| 49 | ❌ | Chat profanity filter — **retired 2026-11-05**: YouTube owns moderation (blocked words + hold for review run before the API). Replaced by not rendering YouTube's moderation markers (tombstones / no-display events) | [`task-49`](TASKS/task-49-chat-profanity-filter.md) |
| 50 | ☐ | One-button publish of the local recording (resumable upload) | [`task-50`](TASKS/task-50-publish-recording.md) |
| 54 | 🔶 | Freemium: EULA + privacy shipped; one codebase for free Demo + paid app. **Blocked on Valve**: identity verification pending, tax review up to 30 business days; app IDs, store page and build review all follow | [`task-54`](TASKS/task-54-marcom-free-merch.md) |
| — | 🔶 | **Steam ownership** — `BIsSubscribedApp(paidAppId)` + Debug fake + app-wide `IsAppOwned`; paid app ID awaits Steamworks | [`commercial`](docs/ai/commercial.md) | | — | 🔶 | **Steam ownership** — `BIsSubscribedApp(paidAppId)` + Debug fake + app-wide `IsAppOwned`; paid app ID awaits Steamworks | [`commercial`](docs/ai/commercial.md) |
| — | ❌ | **Polar subscription path** — retired 2026-11-01 with the one-price model. Design kept in `decisions.md`, not built | [`decisions`](docs/ai/decisions.md) | | — | ☐ | **Steam packaging** — `publish` output + `steam_api64.dll`, paid app and Demo, depots, `steamcmd` upload, store pages + capsule assets, measured system requirements | [`Distribution.md`](Distribution.md) |
| — | ✅ | **OutputProfile gate** — resolver + `EncoderOptions` wiring (`Models/OutputProfile.cs`, one ffmpeg/two outputs, dev override); higher tiers follow `IsAppOwned` | [`commercial`](docs/ai/commercial.md) | | 48 | ⏸ | Distribution & packaging (winget/portable) — parked behind the entitlement seam | [`task-48`](TASKS/task-48-distribution-msix.md) |
| — | ✅ | **Demo conversion UI** — owned users get higher tiers; non-owners see greyed tiers, and selecting one opens About with conversion copy and an explicit llamacasty.com link. No browser opens until the link is clicked | [`commercial`](docs/ai/commercial.md) | | — | ☐ | **Training-video series** — 32 subjects listed in `youTubeTrainingVideos.md`; each is documentation, channel content and support deflection at once | `youTubeTrainingVideos.md` |
| — | ✅ | **System-message strip replaced by Logs tab** — current launch's newest 500 raw AppLog entries, newest-first; no prior-launch import or logging-policy changes | [`ui-surface`](docs/ai/ui-surface.md) |
| — | ☐ | **Steam packaging** — `publish` output + `steam_api64.dll`, paid app and associated Demo setup, depot, `steamcmd` upload, store pages + assets | [`Distribution.md`](Distribution.md) |
| — | ☐ | **Training-video series** — capture the first 8–10 recurring questions as videos on `@llamacasty`; each is documentation, channel content and monetization at once | MARCOM → support→content loop |
| 9·5 | ☐ | Webcam identity key reconciliation | [`task-09`](TASKS/task-09-live-stream-management.md) |
| 14 | 🔶 | Creator feedback batch — branch 3+ | [`task-14`](TASKS/task-14-creator-feedback-batch.md) |
| — | ❌ | Retired: `StreamConfig` defaults are unused; encoder output comes from the selected quality tier | [`Models/index.md`](Models/index.md) |
| 36 | ☐ | **Gold pass** — the v1 gate, always the last unit (see 1.0 gates) | [`task-36`](TASKS/task-36-gold-pass.md) |
| 48 | ⏸ | Distribution & packaging — route settled 2026-10-31 (winget/portable first; Steam + Store parked). Build steps: [`Distribution.md`](Distribution.md); starts after the entitlement seam | [`task-48`](TASKS/task-48-distribution-msix.md) |
| 10 | ❌ | Retired 2026-10-08; superseded by the TASK 54 rows above — the Polar design it described is rebuilt as a lease (`commercial.md`), not revived as written | [`task-10`](TASKS/task-10-monetization.md) |
## Pending the creator's hands (not programming)
- ✅ App Settings grouping + right-click gear — reported working 2026-10-29.
- ✅ Transition picker/durations, Wipe, preview + program transitions — reported working 2026-10-29.
- ✅ Brand logo layer — reported working 2026-10-29.
- ⏳ Vertical-tier camera check (Live tile inside the band, Chat rect-only) — outstanding.
- ⏳ **Webcam cadence**: a Release take in the failing geometry (vertical tier first) blocks Units 1–2.
No fix until a reading names the stage.
## Creator-requested follow-ups (park for Day 6)
- Group controls under **This PC** and **YouTube**.
- ON-AIR opens a preflight using the saved visibility default and an explicit **Start Broadcast — [scope]** action.
- Persistent Help icon immediately left of Edit opens the creator-triggered, app-only 1-2-3 first-broadcast guide. Keep copy top-aligned and high-contrast; never composite the guide into broadcast output.
- Add YouTube category selection; send tags through the video API; support thumbnail upload.
- Add tooltips to settings tabs and make Logs wrapping effective.
## 1.0 gates (before the first shipped build) ## 1.0 gates (before the first shipped build)
- **Release visibility flow** — default to Private; let the creator choose Private/Unlisted/Public - **Release visibility flow** — default to Private; let the creator choose Private/Unlisted/Public at
in the ON-AIR preflight and change it live through `UpdateBroadcast`; keep development/test runs creation and change it live through `UpdateBroadcast`; keep development/test runs forced Private.
forced Private. Hide the separate TEST button as already decided for v1. - **Kill the dev multi-instance affordance** — `Helpers/InstanceProfile.cs` is `#if DEBUG`; re-prove on a
- **Kill the dev multi-instance affordance** — `Helpers/InstanceProfile.cs` is already `#if DEBUG`; Release build.
re-prove it on a Release build. - **Single-instance guard** — Release has none: two launches share one layout DB (read-whole/write-whole)
- **Release engineering** — `scripts/publish.sh` **done** (self-contained win-x64, `ffmpeg\` and one audio pipe. A named mutex that activates the first window.
guarded, no test artifacts), `LlamaCasty.exe` assembly rename **done**, full license texts beside - **Gold pass** (TASK 36).
`THIRD-PARTY-NOTICES.txt` (TASK 4 req 9). Outstanding: Debug-only `InternalsVisibleTo`. - Release engineering: `publish.sh` **done**, exe rename **done**, full licence texts beside
- **Gold pass** (TASK 36): the screens/layers audit, native-Windows verification, and the `THIRD-PARTY-NOTICES.txt` needed; Debug-only `InternalsVisibleTo` outstanding.
branding-flash decision.
## Research ## Research
+52
View File
@@ -474,3 +474,55 @@ These were explicitly ruled out:
default. Tests: `BrandLogoTests`. On-screen check pending. default. Tests: `BrandLogoTests`. On-screen check pending.
- ☐ **Cube** — GPU path gated on capability; CPU path only if it holds 60fps. - ☐ **Cube** — GPU path gated on capability; CPU path only if it holds 60fps.
- Gear: right-click opens App Settings (built, on-screen check pending). Left-click menu stays for now. - Gear: right-click opens App Settings (built, on-screen check pending). Left-click menu stays for now.
---
# 2026-11-09 trim — completed rows and the retired surge plan
Moved out of `TASKS.md` when the queue was refactored (creator ruling: finish the queue, then the new
features). Nothing here is active; the reasons for the two `❌` rows live in `docs/ai/decisions.md`.
## Completed
| Task | Scope |
|---|---|
| 53 | Output-format contract — six size/FPS modes, vertical YouTube playback, recordings visually accepted |
| 3·17 | Text layer — content, font, colour, alignment, drag/resize, persisted (2026-11-04) |
| 21 | Media source — video file decodes, composites, plays audio, loops, stops with last reference (2026-11-05) |
| 64 | Green/Blue Screen — detect + key from the decode pipe, opt-in auto-apply, alert clips included (2026-11-05) |
| 3·20 | Session report — derived from `superChatEvents.list` on demand, never persisted (2026-11-05) |
| 39·2 | YPP slice 2 — three eligibility bars, dated thresholds, "x of 3 criteria met" (2026-11-05) |
| 32 | Stream resilience — fragmented MP4, blip retry, poll-driven RECONNECTING, ingest pre-flight, one-click Back on air |
| 33 | Auto step-down — cause-classified (machine → shed, fps, resolution; upload → bitrate), one-way, floors |
| — | Preview undo (Ctrl+Z / Ctrl+Shift+Z) |
| — | OutputProfile gate + Demo conversion UI |
| — | System-message strip replaced by the Logs tab |
## Retired rows
| Task | Why |
|---|---|
| 49 | Chat profanity filter — YouTube owns moderation (2026-11-05) |
| 10 | Polar subscription path — superseded by the one-price model (2026-11-01) |
| — | `StreamConfig` defaults — no production call sites |
| 34 | Scheduled streams — retired 2026-11-09, platform's surface |
| 50 | Publish recording — retired 2026-11-09, platform's surface |
## The surge plan (2026-11-02 → 11-07) — superseded
Six days of assigned work, written before the queue was re-ordered. Kept for the record only; the live
order is in `TASKS.md`:
| Day | Units |
|---|---|
| 1 | Licensing: ownership check, greyed tiers, About conversion, Logs tab |
| 2 | Release blockers: visibility flow, 30/60-fps gate, vertical resolution, webcam identity, Move |
| 3 | Resilience: TASK 32 + 33 |
| 4 | Content & media: text layer, media wiring, profanity filter (retired), scheduled streams (retired) |
| 5 | Data & settings: reward persistence, YPP slice 2, defaults split, scene audio, settings round, disk hygiene |
| 6 | Finish: publish recording (retired), undo, feedback batch, gold pass, sweep |
## Creator hands — cleared 2026-11-09
- Vertical-tier camera check: 1–6 pass (Live tile inside the band, Chat rect-only, 16:9 restored exactly).
- Webcam cadence, vertical geometry: 29.8 distinct images/s — the 25.6/s failure does not reproduce.
+12
View File
@@ -22,3 +22,15 @@
13. ✅ Web source URI hidden for webcam — URI section was showing for webcam due to WPF binding path `SelectedElement.Type` not resolving on `WebcamSceneConfig`; replaced with `IsWebSource` virtual property on `SceneElement` (mirrors existing `IsWebcam` pattern), overridden in `Source` to return `Type == SourceType.WebSource`, bound via `BoolToVis` 13. ✅ Web source URI hidden for webcam — URI section was showing for webcam due to WPF binding path `SelectedElement.Type` not resolving on `WebcamSceneConfig`; replaced with `IsWebSource` virtual property on `SceneElement` (mirrors existing `IsWebcam` pattern), overridden in `Source` to return `Type == SourceType.WebSource`, bound via `BoolToVis`
14. ✅ Check/revert consistent order + accept — all three element sections (webcam border, countdown timer, web URI) now use consistent [✕][✓] button order; green check buttons enabled with click handlers that save current values as snapshot baseline (so revert undoes to last accepted state, not initial selection) 14. ✅ Check/revert consistent order + accept — all three element sections (webcam border, countdown timer, web URI) now use consistent [✕][✓] button order; green check buttons enabled with click handlers that save current values as snapshot baseline (so revert undoes to last accepted state, not initial selection)
### Branch 3 — queued 2026-11-09 (creator testing notes)
| # | Slice | Why |
|---|---|---|
| 15 | ☐ **Mic input gain** — a dB stage *ahead of* the gate, meter reads post-gain | A headset mic sits close enough to clear the gate; a table-mounted boom is quieter and the gate eats it. Not AGC — automatic gain pumps on breath and silence. Clipping is already the limiter's job |
| 16 | ☐ **Webcam blank / standby** — a visibility hotkey for the webcam layer (tenth registration), optional standby media while hidden | "My webcam has to go away *now*." The privacy blur covers the screen case; this covers the camera in one keystroke |
| 17 | ☐ **Opacity + geometry consistency** — every layer type exposes the same opacity control and the same position/size affordances | Parity across layer types. Geometry fields stay, but collapse behind a "Geometry" disclosure: keyboard-only and screen-reader users cannot drag a layer, and precise alignment between two monitors is otherwise guesswork |
| 18 | ☐ **Show in folder** — on the stop notification, reveal the finished recording | Publishing is retired to YouTube (TASK 50); the app's remaining job is "where did my file go?" |
One Good Dog test per slice. If any of them turns out to be a redesign rather than a fix, it gets its own
task number instead of hiding in this batch.
+65
View File
@@ -0,0 +1,65 @@
# TASK 65 — Replay clips: buffer the last 15 seconds, show it, save it
> Catalog: [`TASKS.md`](../TASKS.md) — status and requirements live here.
> Spec settled with the creator 2026-11-09. **Not a Twitch-clip clone:** the platform owns viewer
> clips; this is the creator-side replay buffer YouTube does not give you.
## The rule
**15 seconds is a clip.** Options 10 / 15 / 20, default **15**, no slider, no 30. A clip is a moment,
not a segment — 3-minute Shorts are the platform's problem, not ours.
## Why the ring holds encoded bytes, not frames
A 1080p BGRA composite frame is 8.3 MB; fifteen seconds at 60 fps is **~7.4 GB** of raw frames. The
15 MB figure is *after* compression. So the ring keeps **fragmented MP4** (`frag_keyframe+empty_moov`)
that ffmpeg writes to a pipe, in a **byte ring in RAM** — never on disk, never raw.
- Fragments are **1 s** (not 2): the ring can only hand back whole fragments, so a longer fragment
means the save can miss the punchline.
- The buffer is capped **by bytes**, not seconds — bitrate moves. When 15 s would exceed the cap, the
clip is simply shorter.
- The ring is a member of the process: it dies with the app, so a crash leaves no debris and needs no
sweep. **Nothing touches the disk until F10.**
## Trigger
**F10** saves the clip (F1–F9 are taken; F11 is the fallback if a game steals F10). No focus switch, no
mouse. Deferred deliberately: **voice triggering** (always-listening in a streaming app is a privacy
liability, and the game's own audio produces false positives) and **`!clip`** (only for viewers/mods,
never the creator, who cannot reach a keyboard mid-moment).
## The replay layer
One layer type, added by the creator to the scenes they want; behaviour comes from the scene:
| Scene | Behaviour |
|---|---|
| **Live** | plays the new clip **once**, then **hides** — you're there to see it or you're not |
| Every other scene | loops the last **1–3** saved clips **in order** (highlight reel), configurable |
- **Audio off by default**, toggleable per layer. The show's sound design is the creator's; a replay
should not re-mix it uninvited.
- **The replay layer is excluded from the clip's own composition.** Otherwise saving while a clip plays
records a video of a video (hall of mirrors). Viewers still see it live; the clip simply doesn't.
## Storage and retention
- Saved to `clips\` beside recordings, as a normal `.mp4`.
- **The three most recent are kept**; the fourth deletes the oldest.
- **Auto-clip on monetization events** (off by default): a Super Chat / membership / raid saves the
preceding 15 s hands-free. Same retention, so it cannot accumulate.
- **Disk discipline:** no steady writes while live (the ring is RAM); the free-space guard that already
protects REC also disables the ring when space is low — **the recording always wins**.
## Privacy
Named in `PRIVACY.md`: clips are local only, the three most recent are kept, older ones are deleted
automatically, and nothing is uploaded. The first save says the same in one line, so the policy is not
only a document.
## Tests
- Ring: fragment boundaries respected, byte cap enforced, save emits a playable MP4, oldest pruned.
- Layer: once-then-hide in Live, in-order loop elsewhere, silent by default, layer excluded from the clip.
- Failure: save with an empty ring (just started) says so instead of writing a 0-byte file.
+17
View File
@@ -0,0 +1,17 @@
# TASK 66 — Timed ticker messages (the ticker's real job)
> Catalog: [`TASKS.md`](../TASKS.md). Creator idea 2026-11-09.
**Problem.** The ticker today is wired to YouTube events that a new streamer will rarely see — no
memberships, no Super Chats, nothing to tick. A feature that fires twice a year is furniture.
**Idea.** Give the ticker its own clock: a list of creator-written messages plus an interval.
- Config: interval (minutes) and a list of lines; rotate in order, newest config wins on restart.
- Example the creator gave: *every 20 minutes, promote the merch channel*.
- Yields to an alert: an event's own ticker beats a scheduled one, and the clock resumes after.
- Off by default, one message to start, no randomisation and no scheduling-per-day until asked.
**Where:** the ticker section of the alert box properties.
**Tests:** interval firing, rotation order, alert pre-emption, persistence across restart.
+16
View File
@@ -0,0 +1,16 @@
# TASK 67 — Per-event alert videos
> Catalog: [`TASKS.md`](../TASKS.md). Creator idea 2026-11-09.
**Problem.** The app plays one alert clip for every event. That single clip was always a placeholder.
**Idea.** A clip per event kind — Super Chat, Super Sticker, membership, subscription, gift, raid —
falling back to the default when a kind has none assigned.
- Each slot accepts a local file; the existing picker and the decode/keying pipe are reused unchanged
(TASK 21 / 64), including green/blue screen — a clip is a clip.
- Missing slot → default clip → nothing, in that order, and never an error for the creator who only
filled one.
- The alert box's display method, sound byte and ticker stay per-event as they already are.
**Tests:** slot selection per event kind, fallback chain, a keyed per-event clip composites correctly.
+28
View File
@@ -0,0 +1,28 @@
# TASK 68 — Editing while live, and the first-run On-Air state
> Catalog: [`TASKS.md`](../TASKS.md). Creator rulings 2026-11-09.
## Part 1 — edit while live
Creators need to change a layout mid-show: hide a layer, nudge a camera, swap a video. Today editing is
gated to "not live", so the only way is to stop the show.
- Allow the layer panel and geometry edits while streaming/recording.
- An edit becomes program immediately — one canvas, no preview/program split — so an accidental drag is
visible to viewers. Undo (Ctrl+Z) must therefore be live too, and the UI must say when editing is live.
- Preview-mode gestures stay as they are: left = zoom while live, right = menu; **no chord while live**.
Editing live is an explicit mode the creator turns on, not something a stray click does.
- Never allow: removing the Background, deleting a scene that is on air, changing the output tier's
composition mid-frame.
## Part 2 — first-run state
The app should open in **On-Air mode** to match the 1-2-3 screen, so the first thing the creator sees
is the state they will stream from.
Risk to settle before building: an armed ON-AIR pill changes what Start does. The 1-2-3 screen must
*explain* the state rather than let the app default into the riskier action silently — a creator who
expects a recording must not go live by pressing Start.
**Tests:** edit during a session changes the composite; undo restores; a blocked edit action reports and
does nothing; first-run state matches the guide's copy.
+17 -3
View File
@@ -219,6 +219,20 @@ Do not build these; each was explicitly ruled out.
(<https://stackoverflow.com/questions/44346893/>), so no client-side moderation machinery is built on them. (<https://stackoverflow.com/questions/44346893/>), so no client-side moderation machinery is built on them.
- Virtual camera output — a different product and a device-driver swamp; our output is one - Virtual camera output — a different product and a device-driver swamp; our output is one
YouTube stream. YouTube stream.
- Replay buffer, stream clipping, multi-destination restreaming, an in-app bug reporter, and an - Multi-destination restreaming, an in-app bug reporter, and an advanced broadcast-form tab
advanced broadcast-form tab (latency/DVR/embed/projection/CC/region are fixed at sane defaults; (latency/DVR/embed/projection/CC/region are fixed at sane defaults; every exposed field is a support
every exposed field is a support ticket). ticket).
- **Replay buffer — REVERSED 2026-11-09 (TASK 65).** It was out on "different product" grounds; the
creator's case won: YouTube gives viewers clips but gives the creator no last-15-seconds buffer, so
this is the app's surface, not the platform's. Scoped tightly: 10/15/20 s, RAM ring of encoded
fragments (raw frames would be ~7.4 GB), three kept, silent by default.
- **Do not rebuild the platform's own surface (2026-11-09).** Scheduling and publishing are retired for
this reason: YouTube owns scheduled broadcasts *and* the reminder that gives them value, and Studio
owns upload (resumable, quota-aware, explains rejections) — reimplementing either would be a worse
copy that can fall out of sync. The app's edges are capture → scene → sound → output; everything past
the file belongs to YouTube. Publishing would also widen the OAuth consent to whole-channel video
management for one convenience.
- **Voice triggering — deferred 2026-11-09.** Not a recognition-quality problem: an always-listening
trigger in a streaming app hears the *game*, so the content is the false-positive source, and the
consent cost is worse than the upload scope we refused. Windows Voice Access or a Stream Deck can
send the keystroke; the app keeps F10.
+3
View File
@@ -66,6 +66,9 @@ launch set already in `MARCOM.md`.
| Layout autosave | "close the app, come back, it is where you left it" | | Layout autosave | "close the app, come back, it is where you left it" |
| Record with no account, no sign-in | "you do not need YouTube to press record" | | Record with no account, no sign-in | "you do not need YouTube to press record" |
| Test stream (private, simulated chat) | "practice without an audience" | | Test stream (private, simulated chat) | "practice without an audience" |
| **Replay clips** — F10 saves the last 15 s; a layer replays it (once in Live, a looping reel elsewhere) | "clip that" — the moment nobody had to leave the keyboard for |
| **Timed ticker messages** — every N minutes, your own line | "promote something without remembering to" |
| **Per-event alert videos** — a different clip per event kind | "your Super Chat gets its own moment" |
| **Resilience**: ingest pre-flight (dead network never creates a ghost broadcast), reconnect without ending the stream, one-click **Back on air**, crash-safe fragmented MP4 | "the network dropped and the stream came back" — proof beats a feature list | | **Resilience**: ingest pre-flight (dead network never creates a ghost broadcast), reconnect without ending the stream, one-click **Back on air**, crash-safe fragmented MP4 | "the network dropped and the stream came back" — proof beats a feature list |
| Chat overlay (fonts, colours, fade, never stored) | privacy + looks | | Chat overlay (fonts, colours, fade, never stored) | privacy + looks |
| Alerts: built-in clip, custom video, sound byte, ticker | "your Super Chat moment" | | Alerts: built-in clip, custom video, sound byte, ticker | "your Super Chat moment" |