docs: rewrite HANDOFF for session end; import the TASK 21 picker spec from the old handoff into TASKS.md

The picker-slice deliverables + loop-provider rule lived only in HANDOFF (rewrite-every-
session = lose-it). Moved into TASK 21 where it's durable; HANDOFF rewritten: locked
decisions 1-9, queue order, landmines (incl. the 2 unpushed docs commits), no-suggestions
note carried.
This commit is contained in:
2026-09-01 19:04:21 -07:00
parent e1d8b10388
commit 711ba54af7
2 changed files with 83 additions and 93 deletions
+56 -93
View File
@@ -2,107 +2,70 @@
## Branch / Commit State ## Branch / Commit State
**`main`**, clean working tree, **fully pushed to `origin/main`** (nothing pending). **`main`**, clean working tree, **2 local commits NOT yet pushed** (waiting for the user's "push"):
Last work boundary: `097dd0d` (docs: hand off TASK 21 UI picker slice). No uncommitted - `e1d8b10` — docs: v1 = feature-complete ruling — queue TASKs 32–36, close out-of-product list, fix map lies
work; no local commits ahead of origin. - `7c6ec3e` — docs: pre-GA posture (visibility lock rationale, flash preview-only + escalation, audit → gold pass)
Recent commit series (all pushed, before this session's end): No code changed this session — docs only; no build/test run needed.
- `097dd0d` — docs: hand off TASK 21 UI picker slice (acquisition + loop wiring spec)
- `881addb` — TASK 21 slice 3: loop control in MediaVideoSource (process factory + Loop flag)
- `a2219be` — TASK 21 slice 2b: native-FPS pacing in MediaVideoSource (probe -> delay seam)
- `8fa7842` — TASK 21 slice 2a: native-FPS probe seam (ffprobe parse + derive sibling)
- `071ec8b` — TASK 21 slice 1 step 4: wire media into resolver + preview routing
- `480ead0` — TASK 21 slice 1 step 3: IMediaFrameSource + MediaVideoSourceManager
- `a1d9751` — docs: ffmpeg decode-contract verification recipe + WSL CLR boundary
- `8f49010` — TASK 21 Increment B: FFmpeg rawvideo video decoder -> VideoFrame
## Why the session ended ## What this session was
User ready for a break after a substantial day. No mid-flight work — everything is The creator's product/PM audit: "the refactor lost institutional knowledge — potholes and gone
committed and pushed. This is a clean stopping point. streets." Walked the whole map, cross-checked claims against code, and wrote the results back.
Nothing was coded. The queue below IS the outcome.
## What's In Flight ## Decisions locked (2026-09-01 — creator rulings, do not re-litigate)
Nothing actively mid-flight. The last TASK 21 work unit (the **UI picker / acquisition 1. **v1 = the finished product. No v1.x.** Everything queues to v1 or to the closed
slice**) is **handed off**, not in progress — spec reproduced in the section below. "Out of product — permanently" list (TASKS.md, file end). 10% margin = that written list.
It is a GUI feature that must be built + verified natively on Windows. 2. **TASK 32 Stream resilience** — IN: blip retry (app-side; ffmpeg does NOT self-reconnect RTMP
output — map fixed), measured-grace countdown on the ON-AIR sign (amber RECONNECTING → red
OFF AIR → button "● Back on air"), one-click new broadcast reusing saved form, crash-safe
fragmented-MP4 recording, TCP pre-flight. **Dead-broadcast RESUMPTION IS OUT** — API-impossible,
creator ruled; no lying in copy.
3. **TASK 33 auto step-down** — v1 (the map claimed it existed; it never did — claim fixed).
Pure policy, one-way per broadcast, rides TASK 32's restart machinery.
4. **TASK 34 scheduling** — drawer version (☑ Scheduled + datetime in the Text drawer; Save CREATES
the broadcast; uncheck deletes; adoption at Start). Two research flags pending (early-push
behavior; delete semantics) — cite before building. Dark until the visibility unlock.
5. **TASK 35 scene-linked audio** — spec approved as written (nullable per-scene mute flags,
applied once on live transitions only).
6. **TASK 36 gold pass** — the ONLY place these happen: visibility unlock, flash-live enable,
screens/layers audit (the 2026-08-22 landmine, retired here), native-Windows verification suite,
expiry reminders (T-14/7/1 toasts; research whether Polar validate carries `expires_at`),
signing/installer/EULA/Velopack. Compile flags capped at `HARDENED` + `MOCK_REWARDS`,
additive-only, CI-compiled (DEV_LICENSE_BYPASS rejected).
7. **Branding flash** = the nagware: escalating cadence (curve = build-time knob), **preview-only
while pre-GA** (never on test output/recordings), paid removes it — that stays the only paid delta.
8. **Private-only lock stays** (test-phase channel protection; DVR/VOD on = invisible review tapes).
9. **Alerts are free** — stale "one paid feature" language corrected everywhere.
**2026-09-01 — doc-only work unit (monetization awareness spec'd into the map):** NOT ## What's next (the queue, in order — per TASKS.md "v1 execution order")
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) **TASK 21 UI picker slice** is first — the handoff spec from 2026-08-31 is still valid and reproduced
in TASKS.md/TASK 21 (file picker + Acquire/Release wiring + `MediaIsLooping`→`Looping` rule +
loop/volume UI). GUI work — build + verify on native Windows. Then TASK 32 slices, 33, 34, 35,
text source (TASK 3 item 17), the monetization chain (capture → report → journey → Alerts),
MARCOM (TASK 13), and TASK 36 last.
The user revisited the ongoing "how code-complete are we" question. Current agreed read: ## Landmines
- **Feature/code completeness ≈ 88–92%**, after removing from consideration: - **Push state**: 2 docs commits local-only. Trust `git log`, not this file's mood, about what's pushed.
- **TASK 13 = MARCOM, not code** (social media launch kit) — does not count against code-complete. - A stale `testhost` can lock `ytLive.Tests`'s `ytLive.dll` → MSB3027; kill it first
- **TASK 10 (monetization / Polar billing / Velopack distribution + updater) — deliberately (`cmd.exe /c "taskkill /PID N /F"`).
parked by the user until it is the dead-last thing to do.** Not a blocker for feature completion. - Full-suite vstest hangs headless (RealAppHost/WASAPI) — run test classes individually; GUI suites
- Remaining real code before feature-complete: verify on the native desktop.
- **TASK 21 UI picker** slice (the handoff below) — the one genuinely open feature unit. - **WSL cannot spawn Linux ffmpeg from the Windows CLR shim** (Win32Exception 193) — verify ffmpeg
- **TASK 9** — visibility picker (drop the always-Private lock, let user pick contracts via shell/python (recipe in MyMistakes.md); CLR→real-ffmpeg is Windows-suite territory.
Private/Unlisted/Public) + a couple stream-management items. Linux static ffmpeg 7.0.2 + test clip live at `/tmp/opencode/`.
- **TASK 4** — background removal (excluded by design) + a few capture-pipeline items. - Pre-commit gate: `./scripts/verify.sh "<files>"` (clean build, 0 warnings — incremental builds lie).
- **Biggest actual risk is not code — it is the missing native-Windows verification pass.** - Real-`MainWindow` tests MUST use `MainViewModel.LayoutPathOverride` (temp DB) or they wipe the
Real ffmpeg media decode/loop/pacing, recording, and the GUI suites have only ever run user's real layout on shutdown (see ai.md → Tests).
headless in WSL or against fakes. Crossing-the-line "works on Windows" readiness is lower - The two known pre-existing failures (`AudioPipelineTests.Mix_HonorsProviderGains…`,
than the code-complete number would suggest until that pass happens. `RoundClipInteractionTests.Round_Clip_Corner_Is_Grabbable…`) are NOT regressions.
## @ User note — no suggestions for the time being ## @ User note — no unsolicited roadmap
The user asked to **stop with forward-looking suggestions** (they read as distracting). Do The creator asked to **stop with forward-looking suggestions** unless asked. The queue above is
not volunteer next-step options or "what's next" lists unless the user asks. Answer what is theirs, from the 2026-09-01 audit — work what's queued, report what changed, stay quiet on
asked; stay quiet on roadmap unless prompted. "what's next" beyond the queue. They prefer short, spaced-out output over dense text.
## UI picker slice — handoff spec (2026-08-31)
Nothing `AcquireAsync`s a media path yet, so **no frames flow in a running app** —
the decoder, manager, pacing, and loop mechanism are all shipped and unit-tested,
but a media Source has no way to start a session. Deliverables, in order:
1. **File picker + add/remove commands** (mirror `MainViewModel.Background.cs`/`Trax.cs`
`OpenFileDialog` usage — see `MainViewModel.Trax.cs:68`). A "Media" source action
opens an `OpenFileDialog` filtered to video (mp4/mov/avi); on OK set `Source.MediaPath`,
add to scene, and **`AcquireAsync(MediaPath)`**; on remove **`ReleaseAllAsync(MediaPath)`**.
On `StagedScene` layout loads/teardown, call `ReleaseAllAsync` for every media path no
longer present (and acquire new ones) so sessions track the live scene.
2. **Wire `Source.MediaIsLooping` → `IMediaFrameSource.Looping`** — needs a manager-level
per-**path** loop provider (see ambiguity: sessions are refcounted per `MediaPath` and
shared across scenes, but `MediaIsLooping` is per-`Source`; a scene set → the path's
session `Looping` = any live reference has it on, so one scene turning it on while
another shares the file is shared behaviour — pick a rule and record it).
3. **Loop toggle + volume slider** in the source context menu / inspector (TASKS 11/10).
4. `MediaVolume`/`MediaPlaybackState` wiring (audio path for media is future work).
**Design notes to preserve:** `ResolveOutputFrame` reads `_mediaManager.GetLatestFrame(MediaPath)`;
`OnMediaPreviewBitmapChanged` adopts the shared `WriteableBitmap` per path; `MediaFailed` →
`OnMediaFailed` (currently `Debug.WriteLine`, surface in UI). Composer scales any frame size
(media renders through the generic `frameFor(element)` path — no compositor change needed).
The real-ffmpeg decode/loop/restart path is only exercised natively on Windows — first
GUI smoke test should add a short mp4, confirm it plays and previews, then crash further.
## Landmine
- A stale `testhost` can lock `ytLive.Tests`'s `ytLive.dll` and break `dotnet build` of the
test project — kill it first (`cmd.exe /c "taskkill /PID N /F"`) on MSB3027.
- Full-suite vstest hangs headless (RealAppHost/WASAPI) — only filtered pure tests run in
WSL; user verifies the GUI suites on native Windows PowerShell (which may also hang if
WASAPI startup blocks — pre-existing, not this change).
- **WSL can only run the Windows-bound dotnet CLR shim** — it cannot spawn Linux ffmpeg/ffprobe
(Win32Exception 193). Verify ffmpeg contracts via shell/python; leave CLR→real-ffmpeg to the
native Windows suite. Linux static ffmpeg/ffprobe 7.0.2 live at
`/tmp/opencode/ffmpeg-7.0.2-amd64-static/`; `/tmp/opencode/clip.mp4` = 640x360 30fps 1s.
## The directive (2026-08-31, user)
Rewrite the project into functional components to aid AI retrieval —
`Services/ChatOverlayLayer.cs` / `SceneGraph.cs` style (owner-state extraction),
not line-count chasing. Audio assumption is now an explicit contract (README
"Audio Assumption"): the app uses system defaults, never fights Windows device
locking, and does not debug user audio issues — OS's problem, not ours.
+27
View File
@@ -1082,6 +1082,33 @@ The tasks below are ordered by dependency and risk. Each task builds on the prev
- **This pairs with TRAX** — TRAX is background music, media source is background video. Together they make non-Live scenes (Starting/BRB/Ending) feel polished. - **This pairs with TRAX** — TRAX is background music, media source is background video. Together they make non-Live scenes (Starting/BRB/Ending) feel polished.
- **Not a full NLE** — no trimming, no multi-track, no effects. Just "play this video in the scene." - **Not a full NLE** — no trimming, no multi-track, no effects. Just "play this video in the scene."
### UI picker slice — spec (imported from HANDOFF 2026-09-01 so it can't be lost by a rewrite)
Nothing `AcquireAsync`s a media path yet, so **no frames flow in a running app** — decoder, manager,
pacing, and loop mechanism are shipped and unit-tested, but a media Source has no way to start a
session. Deliverables, in order:
1. **File picker + add/remove commands** (mirror `MainViewModel.Trax.cs:68` `OpenFileDialog` usage).
A "Media" source action opens an `OpenFileDialog` filtered to video (mp4/mov/avi); on OK set
`Source.MediaPath`, add to scene, and **`AcquireAsync(MediaPath)`**; on remove
**`ReleaseAsync(MediaPath)`** (refcounted — not `ReleaseAllAsync`, unless the path leaves the
layout entirely). On `StagedScene` layout loads/teardown, release every media path no longer
present and acquire new ones so sessions track the live layout.
2. **Wire `Source.MediaIsLooping` → `IMediaFrameSource.Looping`** — needs a manager-level per-**path**
loop provider. Ambiguity to rule + record: sessions are refcounted per `MediaPath` and shared
across scenes, but `MediaIsLooping` is per-`Source`. Chosen rule: the path's session `Looping` =
**any** live reference has it on (shared behaviour). Record it in the slice commit.
3. **Loop toggle + volume slider** in the source context menu / inspector (items 10/11).
4. `MediaVolume`/`MediaPlaybackState` wiring — audio path for media is future work (desktop loopback
already carries it, TRAX-style; `MediaVolume` slider can drive the local file's audio separately
later — out of this slice).
**Design notes to preserve:** `ResolveOutputFrame` reads `_mediaManager.GetLatestFrame(MediaPath)`;
`OnMediaPreviewBitmapChanged` adopts the shared `WriteableBitmap` per path; `MediaFailed` →
`OnMediaFailed` (currently `Debug.WriteLine` — surface in UI via toast, TASK 24 stack). The compositor
scales any frame size — media renders through the generic `frameFor(element)` path, no compositor
change. First GUI smoke test: add a short real mp4 on native Windows, confirm it plays and previews.
--- ---
## TASK 22 — Audio sync offset ## TASK 22 — Audio sync offset