finishing AIs work because tokens

This commit is contained in:
2026-09-01 07:27:35 -07:00
parent 4a321b19ff
commit 8fa54d2de7
3 changed files with 53 additions and 2 deletions
+9
View File
@@ -27,6 +27,15 @@ Nothing actively mid-flight. The last TASK 21 work unit (the **UI picker / acqui
slice**) is **handed off**, not in progress — spec reproduced in the section below. slice**) is **handed off**, not in progress — spec reproduced in the section below.
It is a GUI feature that must be built + verified natively on Windows. It is a GUI feature that must be built + verified natively on Windows.
**2026-09-01 — doc-only work unit (monetization awareness spec'd into the map):** NOT
code — the user asked to integrate the built-in monetization-awareness requirement into
the existing docs as precursor/related work, then said "exec". Done: `ai.md`
(Monetization section + Tests note), `TASKS.md` (TASK 10 "Related work" + the two Alerts
items), and `MONETIZATION.md` (gitignored; product stance that analytics is free & ungated).
No code changed; no task numbers added (kept as related work per the no-expand-queue rule).
Whether this stays a docs-only change or seeds the first code slice (reward-event capture)
is the user's call — nothing in flight until decided.
## Project-wide code-complete picture (2026-08-31, user's framing) ## Project-wide code-complete picture (2026-08-31, user's framing)
The user revisited the ongoing "how code-complete are we" question. Current agreed read: The user revisited the ongoing "how code-complete are we" question. Current agreed read:
+12 -2
View File
@@ -119,7 +119,7 @@ Lifecycle: `created → ready → [testing] → live → complete` (transitional
17. ☐ **Text source** — live text ("Starting soon", "Back in 5", handle, callout) 17. ☐ **Text source** — live text ("Starting soon", "Back in 5", handle, callout)
18. ✅ **Chat box** — YouTube live chat rendered *on* the stream so viewers read along in-video — `ChatBoxRenderer` (WPF FormattedText → VideoFrame), configurable font size/color/badges/timestamps/max-messages via Elements panel, schema v10, persisted across save/load 18. ✅ **Chat box** — YouTube live chat rendered *on* the stream so viewers read along in-video — `ChatBoxRenderer` (WPF FormattedText → VideoFrame), configurable font size/color/badges/timestamps/max-messages via Elements panel, schema v10, persisted across save/load
19. ❌ **Background removal (milestone 2)** — ONNX Runtime + DirectML, MediaPipe Selfie Segmentation — deliberately NOT in this build 19. ❌ **Background removal (milestone 2)** — ONNX Runtime + DirectML, MediaPipe Selfie Segmentation — deliberately NOT in this build
20. ☐ **Alerts** — Super Chat / membership / subscribe pop-ins; build after the six; **the one paid feature** (see Monetization in `ai.md`) 20. ☐ **Alerts** — Super Chat / membership / subscribe pop-ins; build after the six; **the one paid feature** (see Monetization in `ai.md`). **Precursor — reward-event capture (2026-09-01):** Alerts render from the app's canonical `RewardEvent` feed (all 7 `liveChatMessage` reward types, persisted to SQLite + the session report — see TASK 10 → "Related work — monetization awareness"), so it consumes that already-built data rather than re-integrating `liveChatMessages`. That capture is the required prior milestone.
21. ✅ **Show Desktop toggle** — `Source.ShowDesktop` bool persisted in DB (schema migration + LayoutStore read/write). When checked, primary monitor captures regardless of fullscreen game state. Toggling off falls back to running game or releases to static placeholder. `ClearBackdropCaptureAsync` clears `CaptureKey` + `VideoImageSource` so static fallback renders. Toggle on Live backdrop context menu (IsCheckable MenuItem). 21. ✅ **Show Desktop toggle** — `Source.ShowDesktop` bool persisted in DB (schema migration + LayoutStore read/write). When checked, primary monitor captures regardless of fullscreen game state. Toggling off falls back to running game or releases to static placeholder. `ClearBackdropCaptureAsync` clears `CaptureKey` + `VideoImageSource` so static fallback renders. Toggle on Live backdrop context menu (IsCheckable MenuItem).
22. ✅ **Top bar — status light + avatar + Log In button** — red/green/pulsing ellipse (disconnected/connected/live). Avatar border visible only when connected, loads via `BitmapImage` code-behind. "Log In" button visible when disconnected, calls `StartStreamCommand` → GoLiveWindow. `ShowStartStream` now requires `IsConnected`. 22. ✅ **Top bar — status light + avatar + Log In button** — red/green/pulsing ellipse (disconnected/connected/live). Avatar border visible only when connected, loads via `BitmapImage` code-behind. "Log In" button visible when disconnected, calls `StartStreamCommand` → GoLiveWindow. `ShowStartStream` now requires `IsConnected`.
23. ✅ **Chat preview — one-at-a-time mock messages** — `RunMockChatPreviewAsync`: simple async loop, displays `MockChatMessages[n]` via `Take(n+1)`, 1000ms between messages, wraps at end. `ChatPreviewEnabled` property on Source (default true). Stops on real messages, restarts on fade timer clear. 23. ✅ **Chat preview — one-at-a-time mock messages** — `RunMockChatPreviewAsync`: simple async loop, displays `MockChatMessages[n]` via `Take(n+1)`, 1000ms between messages, wraps at end. `ChatPreviewEnabled` property on Source (default true). Stops on real messages, restarts on fade timer clear.
@@ -148,7 +148,8 @@ the hot few and nothing esoteric. If a user needs more, they've graduated to OBS
6. **Chat box** — YouTube live chat rendered *on* the stream so viewers read along in-video. YT-native. 6. **Chat box** — YouTube live chat rendered *on* the stream so viewers read along in-video. YT-native.
7. **Alerts** — Super Chat / membership / subscribe pop-ins. The dopamine source. **The one big lift** 7. **Alerts** — Super Chat / membership / subscribe pop-ins. The dopamine source. **The one big lift**
(Super Chat event streaming + on-stream rendering/animation); build after the six. **Also the one (Super Chat event streaming + on-stream rendering/animation); build after the six. **Also the one
paid feature** — see Monetization in `ai.md`. paid feature** — see Monetization in `ai.md`. Consumes the canonical `RewardEvent` feed (reward-event
capture, TASK 10 → related work).
Deliberately NOT supported: game capture, browser source, media playlist, VLC, color-key voodoo, MIDI. Deliberately NOT supported: game capture, browser source, media playlist, VLC, color-key voodoo, MIDI.
@@ -710,6 +711,15 @@ the validator → persisted), compositor bar overlay (top/bottom + above-flash),
**Goal:** annual subscription via Polar.sh that removes the branding watermark. All features are **Goal:** annual subscription via Polar.sh that removes the branding watermark. All features are
free — the only difference between free and paid is the watermark. free — the only difference between free and paid is the watermark.
**Related work — monetization awareness (2026-09-01, ungated, free):** this task's billing is the
*payer* side; the *product* also carries built-in, ungated monetization awareness for every creator —
reward-event capture (all 7 `liveChatMessage` reward event types → canonical `RewardEvent` → SQLite
persistence), per-session/broadcast reports (members, Super Chat $, stickers, gifts, milestones — the
in-app replacement for the emailed "stream activity report"), and a journey tracker (position % +
ETA vs. versioned, date-aware YPP thresholds; the Tier-2 bar doubles for new applicants 2027-02-01).
Details and design notes: `ai.md` → Monetization. This is tracked as related work under this task's
narrative, not a separate task number — it ships in its own code slices with one integration test each.
**Business details (pricing, Polar product/checkout/discounts) in `MONETIZATION.md` (gitignored).** **Business details (pricing, Polar product/checkout/discounts) in `MONETIZATION.md` (gitignored).**
**Polar product:** `d105dfa1-497e-423b-8cd4-e0ee2e3abbc0` (LlamaCasty, $99/yr, currently `private`) **Polar product:** `d105dfa1-497e-423b-8cd4-e0ee2e3abbc0` (LlamaCasty, $99/yr, currently `private`)
+32
View File
@@ -130,6 +130,11 @@ GlobalHotkeyTests + HotkeyConfigTests (TASK 20), WebcamMenuGateTests (TASK 26),
`AudioPipelineTests.Mix_HonorsProviderGains…` + `RoundClipInteractionTests.Round_Clip_Corner_Is_Grabbable…` `AudioPipelineTests.Mix_HonorsProviderGains…` + `RoundClipInteractionTests.Round_Clip_Corner_Is_Grabbable…`
are NOT regressions; see HANDOFF.md. Run test classes individually — the full-suite vstest run hangs.)** are NOT regressions; see HANDOFF.md. Run test classes individually — the full-suite vstest run hangs.)**
Reward-event capture (monetization awareness, see the Monetization section) will add its integration
tests here when it ships: one real chat-poll payload containing all seven reward event types →
assert the persisted canonical `RewardEvent` rows (type + amount/currency/tier/memberLevel/participant
ids) round-trip into SQLite.
### Real-MainWindow tests MUST be hermetic (DB pollution bug) ### Real-MainWindow tests MUST be hermetic (DB pollution bug)
The integration test boots a real `MainWindow` → `MainViewModel` → real `LayoutStore` The integration test boots a real `MainWindow` → `MainViewModel` → real `LayoutStore`
@@ -861,6 +866,33 @@ crooked.
how-tos / feature requests / manual-skimmers. Maintenance cadence = "when I get around to it" with how-tos / feature requests / manual-skimmers. Maintenance cadence = "when I get around to it" with
emergency patches; not a 24/7 service promise. emergency patches; not a 24/7 service promise.
**Monetization awareness (built-in, ungated, free — 2026-09-01):** the app is monetization-aware by
design, for **every** creator, free and ungated (the only subscriber swap remains the branding-flash
removal above). It has three layers, all free — this is the product's differentiator, not a paid tier:
- **Reward-event capture (the data foundation).** The live-chat poll (`YouTubeChatService.Poll`)
already receives every reward event over `liveChatMessages` but today only decodes two
(`superChatEvent`, `newSponsorEvent`); the other five — `superStickerEvent`, `membershipGiftingEvent`,
`giftMembershipReceivedEvent`, `memberMilestoneChatEvent`, `giftEvent` — are silently dropped. The
plan: decode all seven into a canonical `RewardEvent` and persist each to a SQLite table
(`RewardEvents`: broadcastId, type, timestamp, amountMicros, currency, tier, memberLevelName,
gifter/recipient channelIds). `superChatEvents.list` (30-day lookback) backfills prior broadcasts.
This single feed is the foundation that Alerts, the session report, and the journey tracker all read.
- **Session / broadcast report.** A session is created at Go Live; the poll + End roll that session's
reward totals (new members, Super Chat $, stickers $, gifts, milestones). This is the in-app
replacement for the emailed external "stream activity report" — the incumbents (Streamlabs et al.)
email near-useless post-stream summaries; we track the same data natively and maintain it.
- **Journey tracker.** On sign-in/startup, pull current `channels.list?mine=true&part=statistics`
(`subscriberCount`/`viewCount`/`videoCount` — the current OAuth scopes suffice). Compute position
toward each YPP tier threshold and project an ETA curve from the creator's own
per-stream/per-day velocity (subs + watch hours). **YPP thresholds are versioned, date-aware DATA,
never constants** — the Tier-2 bar doubles for new applicants on **2027-02-01** (long-form 4,000 →
8,000 qualified hrs / 365d; Shorts 10M → 20M / 90d), while the Tier-1 fan-funding bar (500 subs /
3,000 hrs / 3M Shorts) is unchanged. Hardcoding the pre-2027 numbers would ship a wrong ETA.
Analytics-API scopes (`yt-analytics.readonly`, additive `yt-analytics-monetary.readonly`) are an
additive capability behind a seam (`IChannelStatsProvider`), not a v1 blocker — current-scope data
is the v1 baseline; the Analytics scopes would require an OAuth re-consent, deferred.
**What was rejected:** always-on watermark (obscurable — replaced by the flash), hard stream-time **What was rejected:** always-on watermark (obscurable — replaced by the flash), hard stream-time
cutoffs (the worst dead end — a stream dying mid-broadcast reads as broken, and YouTube streams cutoffs (the worst dead end — a stream dying mid-broadcast reads as broken, and YouTube streams
routinely run 2-4 hours), soft-limit nagging, freemium feature tiers, and donation-only (relies on routinely run 2-4 hours), soft-limit nagging, freemium feature tiers, and donation-only (relies on