85fad1ab79
Creator ruling 2026-09-27. Criteria, verbatim: "zero headaches, minimal maintenance (for me) while still providing accountability and a reasonably easy upgrade flow." Route A is the only combination where all four are solved by handing the work to Microsoft rather than to a certificate vendor: $0/yr, no certificate, no HSM, no annual renewal, no SmartScreen ramp — plus Store auto-update, Store-side payments/entitlements/refunds/support, and Microsoft review as the accountability layer. The rejected options and their reasons stay in research-store-certification.md §3 so a later session reads the ruling instead of re-deriving it. What this deletes: - The entire licensing backend. PolarLicenseService, PolarLicense, MainViewModel.License.cs (PremiumUrl, customer portal, the OfflineGracePeriod = 14 days subscription-era artifact, renewal/lapse copy) and the wrong "Polar unlocks alerts" string all become dead code. IsPremium is derived from the Store entitlement instead of an HTTP call, which also removes the whole "network flaky -> app thinks I'm expired" bug class. - Velopack, the update URL, and the self-hosted droplet — the Store updates. - Distribution.md's premise: Polar as the distribution backbone, Polar file hosting, and code signing as our problem. The IP-protection sections (1, 5, 6, 7) still stand and the build-posture ceiling is unchanged. What does NOT change: the entitlement. Free gets everything; the branding flash stays the only paid delta. Store IAP changes how IsPremium is obtained, never what it gates. Still open, deliberately: the price. The Store revenue share is unverified (do not assume a percentage), and MONETIZATION.md's $29 -> $49 one-time decision is re-opened against a fresh instinct toward ~$99/yr. No price encoded yet. The first code unit is unchanged: bundle ffmpeg (TASK 48 item 1). That clears the one hard certification gate and fixes a real user-facing 404. MARCOM.md and MONETIZATION.md were edited too but are gitignored by design, so those changes stayed local.
127 lines
8.1 KiB
Markdown
127 lines
8.1 KiB
Markdown
# HANDOFF — current state
|
||
|
||
**Branch:** `main` (pre-1.0, no feature branches — creator ruling 2026-08-24).
|
||
**Last pushed:** `e78c58f` (Store certification research). This unit (recording the Store MSIX
|
||
+ Store IAP ruling) is **docs-only** — no code touched, no build, no tests run.
|
||
|
||
## ✅ DECIDED 2026-09-27 — distribution is the Microsoft Store: MSIX + Store IAP
|
||
|
||
**Creator's criteria, verbatim: "zero headaches, minimal maintenance (for me) while still
|
||
providing accountability and a reasonably easy upgrade flow."** Route A is the only option
|
||
where all four are solved by handing the work to Microsoft rather than to a certificate
|
||
vendor: **$0/yr, no certificate, no HSM, no annual renewal, no SmartScreen ramp**, plus Store
|
||
auto-update, Store-side payments/entitlements/refunds/support, and Microsoft review as the
|
||
accountability layer. The rejected options and their reasons are in
|
||
`TASKS/research-store-certification.md` §3 — **read that before reopening the question, not
|
||
before re-deriving it.**
|
||
|
||
**The big consequence: the whole licensing backend is deleted.** `PolarLicenseService`,
|
||
`PolarLicense`, `MainViewModel.License.cs` (`PremiumUrl`, customer portal, the
|
||
`OfflineGracePeriod = 14 days` subscription-era artifact, renewal/lapse copy) and the wrong
|
||
"Polar unlocks alerts" string all become dead code. `IsPremium` comes from the **Store
|
||
entitlement** instead of an HTTP call — which also deletes the entire "network flaky → app
|
||
thinks I'm expired" bug class. **Velopack, the update URL and the DO droplet go too.**
|
||
|
||
**What does not change: the entitlement.** Free gets everything; the branding flash stays the
|
||
ONLY paid delta. Store IAP changes how `IsPremium` is *obtained*, never what it *gates*.
|
||
|
||
**⏳ Still open: the price.** The Store revenue share is unverified (⚠️ assume no percentage —
|
||
check current terms before setting a price), and `MONETIZATION.md`'s `$29 → $49` one-time
|
||
decision is re-opened against a fresh instinct toward a ~$99/yr subscription. **No price is
|
||
encoded anywhere until the creator settles it.** Note the storefront changed, so the old
|
||
"~$69 floor" and "don't break the launch-price promise" notes now refer to Store tiers.
|
||
|
||
### The first code unit: bundle ffmpeg (TASK 48 item 1)
|
||
|
||
Two real defects the research surfaced — **neither is fixed yet**, this was a docs unit:
|
||
|
||
1. **⛔ `FfmpegLocator` downloads an unsigned exe from GitHub and runs it.** Store policy
|
||
**10.2.2** (dynamic code inclusion) verbatim, *and* the root cause of the 2026-09-01
|
||
failure — the pin aged out of BtbN's 14-day retention and the download **404'd on the
|
||
creator's first real recording attempt**. `FfmpegLocator.cs:30-35` records the incident but
|
||
still reads like a design choice. **Fix: bundle it.** Also deletes the startup network
|
||
dependency.
|
||
2. **⛔ `Distribution.md:318` recommended a $400+/yr EV certificate for a SmartScreen bypass
|
||
Microsoft removed in March 2024.** Fixed last commit — had it shipped, it would have cost
|
||
$400+/yr to buy what $150 buys. Now moot anyway (MSIX is signed by Microsoft), but the
|
||
lesson in `MyMistakes.md` stands: a policy citation is a claim about **scope**, not just
|
||
text.
|
||
|
||
### ⛔ Two invariants that break silently if touched
|
||
|
||
- **Full trust, or recording breaks.** `DefaultRecordFolder()` writes to Downloads/MyVideos;
|
||
that passes through unvirtualized **only** at `mediumIL`. Flip the manifest to
|
||
`appContainer` and output vanishes with no error. A comment is owed there **when the
|
||
manifest lands** (TASK 48 item 6) — not before.
|
||
- **Chat is rendered, never stored.** That non-persistence half is the load-bearing part of
|
||
the 11.12 certification answer. No chat history, moderation log, analytics, or crash payload
|
||
may be added without re-arguing 11.12 first.
|
||
|
||
## Landmines
|
||
|
||
- **Stale fact inside `TASKS.md` (left alone, flagged here per the Scope Lock).** The "1.0 gates"
|
||
section asserts "**TASK 36 is now shipped**", but the catalog row 36 reads **☐ Queued** and
|
||
`task-36-gold-pass.md` still shows items 1–6 unchecked. The line most likely means *item 2*
|
||
(branding flash goes live) shipped at `9761b1d`. **Needs a one-line fix, not a code change** —
|
||
flagging rather than fixing because it is not this unit's job.
|
||
- **A stale `ytLive.exe` (PID 2544) locked `bin/.../ytLive.exe`** and broke `dotnet run` with
|
||
MSB3027. Killed. **If the build fails to copy `ytLive.exe`, check for a running instance first.**
|
||
- **`LayerReorderPersistenceTests.RealMouseDrag_OnTheLayerList_PersistsTheReorder` is flaky by
|
||
environment** (needs an interactive desktop session; 2 fail / 1 pass in isolation). **Run the
|
||
suite with ytLive CLOSED.** Not a regression.
|
||
- **`RealAppHost.RunAsync` exists for a reason** — a frame-pump test MUST `await` inside it. A
|
||
blocking wait occupies the one shared STA thread and **hangs the whole suite with no output.**
|
||
Always background a `vstest` run and poll the log; a foreground piped `vstest` returns nothing
|
||
in this shell even on success.
|
||
- **`GlobalHotkeys`: two instances registering the SAME global hotkey** — Windows refuses the
|
||
second. Left alone deliberately.
|
||
- **MSIX writes under a real package identity are unverified.** The docs say full trust passes
|
||
user-profile writes through, but confirm on a real packaged build before trusting it with
|
||
recordings (TASK 48 item 6).
|
||
|
||
## Test state
|
||
|
||
**367/367 as of `938c5b3`.** Not re-run for this unit — docs only, no code touched.
|
||
|
||
## Uncommitted / untracked
|
||
|
||
- `MARCOM.md`, `MONETIZATION.md` — both edited (privacy-copy guard; the Polar-obsolete banner
|
||
and the re-opened-price note) and both **gitignored by design** (confidential business
|
||
files, `.gitignore:10-11`). They stay local, as intended. Same for `CREDENTIALS.md` —
|
||
**never commit those.**
|
||
|
||
## Next
|
||
|
||
1. **Bundle ffmpeg (TASK 48 item 1)** — unblocked, first code unit, fixes a real user-facing
|
||
failure and clears the one hard certification gate.
|
||
2. **Settle the price** — one-time vs subscription, and the number. Then declare the IAP
|
||
products in Partner Center. **Blocks:** the Store listing, and the `OverlayHost.xaml` copy.
|
||
3. **The packaging project + `Package.appxmanifest`** (TASK 48 items 2–3) — full trust,
|
||
`webcam`/`microphone`, English only; Velopack deleted. Add the `DefaultRecordFolder()`
|
||
comment in the same unit.
|
||
4. **Polar teardown** (TASK 48 item 0) — `PolarLicenseService`, `PolarLicense`,
|
||
`MainViewModel.License.cs`, the `OverlayHost.xaml` string, the `csproj`/`App.xaml.cs`
|
||
Velopack sites. `IsPremium` ← Store entitlement. **Do this as one unit** — a half-torn-down
|
||
licensing path is worse than either end state.
|
||
5. **Verify the Store revenue-share rate** — before step 2, not after.
|
||
6. **Vertical recording verification** — the compositor tier is proven by
|
||
`Render_VerticalTier_Outputs_1080x1920_From_The_Center_Crop`; the *record* path has never
|
||
been run. `FramePump.cs:645` flags off-size tiers as a known follow-up.
|
||
7. **WACK + certification notes + the privacy policy** (TASK 48 items 4–5) — the policy must
|
||
state camera/mic is OS-gated, nothing is retained, and nothing is fetched at runtime.
|
||
8. **Post-session efficacy report** — roll up `CurrentHealth` (dropped frames, duration, health
|
||
message) when a stream or recording ends. `SessionTeardownTests` is the natural home.
|
||
9. **Measure whether the Windows camera toggle gates DShow** — gates all privacy-forward copy
|
||
(`MARCOM.md` guard). Not a certification risk; a trust risk.
|
||
10. **TASK 36:212 stale "shipped" line** — one-line fix, needs a hand.
|
||
|
||
**Dropped from the list** because the ruling made them moot: multi-instance's ffmpeg tools-dir
|
||
race (item 1 makes it disappear), `scripts/publish.sh` + the `LlamaCasty.exe` rename (the Store
|
||
packages and delivers — *revisit only if we ever ship outside the Store*), and the alert-gating
|
||
copy (the wrong string dies with the Polar teardown in step 4).
|
||
|
||
**Everything else in the v1 queue:** stream resilience (32), bandwidth step-down (33), scheduled
|
||
streams (34), scene-linked audio (35), the master limiter (12), text source (3.16), reward
|
||
events (3.20), the media picker (21), webcam identity (9.5), settings units A/C/D (40), and the
|
||
defaults split (37). Ship-checklist items live in `TASKS.md` -> "1.0 gates".
|