feat(entitlement): pivot to paid app ownership

Check Steam ownership of the paid app with BIsSubscribedApp; keep Debug fake ownership out of Release. Ref: https://partner.steamgames.com/doc/api/ISteamApps#BIsSubscribedApp
This commit is contained in:
2026-11-02 09:18:03 -08:00
parent 54902cb838
commit ff5e8d94ae
17 changed files with 229 additions and 217 deletions
+23 -31
View File
@@ -1,40 +1,32 @@
# HANDOFF — 2026-11-01 (exe renamed, depot builds, output rule enforced)
# HANDOFF — 2026-11-02 (Steam ownership check)
## State
- Branch `main`, level with `origin` through `8f79b20`; this unit is local.
- **The shipped exe is `LlamaCasty.exe`** (`AssemblyName` renamed; four pack URIs fixed — the docs
said three, `KeepBroadcastDialog.xaml:5` was the fourth). Guarded by `AssemblyIdentityTests`.
- **`scripts/publish.sh`** stages `artifacts/steam-stage` and refuses to finish if `ffmpeg\`, the
licence, privacy or notices file is missing, or test assemblies leaked in. Output 257 MB.
- **The output-profile rule is built and enforced**: `Models/OutputProfile.cs` (921,600 px / 30 fps,
fail closed) resolved once in `BuildEncoderOptions` — one ffmpeg process, two outputs.
`MainViewModel.DeveloperMode` bypasses it in Debug only.
- **Distribution is Steam-exclusive** (2026-10-31): free app + paid edition as a Steam **DLC**; the
**$10/month** plan sold off-Steam by Polar, *stated, never sold*, in-app. Entitlement is
`steamDlcOwned || polarKeyValid`; the app must start without Steam, falling back to free.
- **Site live with Steam** (`a9d07ce`); `www.llamacasty.com` 301s to the apex.
- `main`; local work unit, not pushed.
- Distribution plan: one paid Steam app ($79 announced launch price, then $99) plus its associated
free Demo. Same code; no subscription, DLC purchase, or GitHub binary.
- `IEntitlement.IsAppOwned` feeds the app-wide `MainViewModel.IsAppOwned` flag. Debug uses a fake
owned result; the fake is excluded from Release.
- Release ownership uses `BIsSubscribedApp(paidAppId)` and fails closed when the ID or Steam is
unavailable. **The paid app ID is not configured yet; the Steamworks application is pending.**
- Steam's private-use clause is not, by itself, a proven blocker. EULA wording should expressly
allow commercial/monetized streaming; OBS is the relevant Steam software precedent. See
`MyMistakes.md` → Steam distribution.
## Next action
**Surge, 2026-11-02 → 11-07 — six days, all remaining code.** Day 1 is partly done ahead of time:
`IEntitlement` + `SteamEntitlement` (Steamworks.NET, fail-closed on every path) + `FakeEntitlement`,
with `HasProOutput` reading the rail. Undo/redo, the rail gear and the EDIT alignment also landed
early. Remaining on day 1: greyed tiers, licence UI's four states, system messages, licence ID in
About. Days 2–6 in `TASKS.md` → "The surge".
Day 1: grey out higher tiers for non-owners and link to the paid Steam page; then the system-message
strip. The queued Debug-only test-access rule also needs checking. Keep one integration test per
change.
## Landmines
- **⛔ `ExcludeFromSingleFile="true"` on the ffmpeg content** — without it the publisher bundles
libav\*.dll into the exe and the app ships unable to find ffmpeg. Recipe in `MyMistakes.md`.
- **Dev affordances are compiled out, not hidden** — `DeveloperPanel` is removed from the Release
build; `Helpers/DevBuild.cs` is the single source for the flag (never `const`).
- **Nothing may gate a feature.** The only paid delta is the output profile; `ShowBrandFlash` stays
`false` and entitlement must never enable it.
- **No checkout inside a Steam build** — the Wallet rule. The monthly is named, never sold, in-app.
- **The org access token never ships.** `organization_id` is public; the token is not.
- **The site publishes the model, never the numbers**, and never takes payment or links a checkout.
- **No self-updater and no version banner**: Steam updates the app.
- Pre-existing: the **full test suite hangs** (WASAPI teardown) — per-class runs; `ytLive.Tests`
carries 3 xUnit warnings the app-only gate does not see.
- Not every hex is a theme colour — `BorderColorPalette`/chat defaults are USER DATA, not tokens.
- The Demo has its own App ID. Ownership must check the paid app ID, never the Demo ID or test ID
`480`.
- No fake ownership code or developer panel in Release.
- No Steam checkout in the app; Steam handles purchase and updates.
- The paid app ID is not available yet; Release stays fail-closed until it is configured.
- `verify.sh` clean app build passes; `EntitlementTests` passes 5/5. Full suite currently has 13–14
UI/content failures; connection to this change is unconfirmed, investigate separately.
- Bundled FFmpeg must stay outside the single-file exe (`ExcludeFromSingleFile="true"`).
- Build with Windows `dotnet.exe`; run tests per class (the whole suite can hang in WASAPI teardown).