docs(commercial): one source of truth for the money — prices, entitlement, channels
The commercial plan was spread over five documents written in five different weeks, and they contradicted each other: product.md sold the Store, decisions.md called the Store dead then re-opened, Distribution.md (736 lines) still described Polar as a download host, MARCOM.md quoted "no subscription" next to a paid tier, and README.md promised "free forever". An outsider could not tell what ships. docs/ai/commercial.md is now the only place money is decided: free = whole app at a 921,600 px / 30 fps output profile covering the recording as well as the stream; Pro = $10/mo or $400 lifetime through Polar, key bound to the buyer's YouTube channel ID via a claim page, 2 activations, 7–14 day lease with offline grace, fail closed, never downgrade a running stream; one portable zip + winget primary, Steam and the Store parked; Azure Trusted Signing (~$9.99/mo), no EV; Founding 200 promo; ~$135/yr costs; anti-piracy posture and what is permanently rejected. Everything else now points at it instead of restating it: - product.md — money sections replaced with pointers; packaging rewritten to winget-first, Store parked; signing updated to Trusted Signing. - decisions.md — graveyard with cause of death for free-forever, the Store ordering, the updater, and the 2026-10-30 reversal of the quality-ceiling rejection. - Distribution.md — rewritten to release mechanics only (artifact, GitHub Releases, winget manifest, signing, checklist, takedown): 736 → 120 lines. - TASKS.md — TASK 54 rewritten, TASK 48's route settled, TASK 10 superseded, five new rows (entitlement seam, output-profile gate, licence UI, system messages, packaging). - README.md — "free forever" replaced with the real split. - capture.md — obs-backgroundremoval is GPL-3.0 and so licence-INCOMPATIBLE since the proprietary EULA; the old "compatible" line was falsified by that change. - TASKS/task-10, task-48, task-51, task-54 — superseded-banner at the top of each, so none of them reads as current policy. - HANDOFF.md — rewritten: plan settled, next code unit is the entitlement seam. MARCOM.md and MONETIZATION.md (gitignored) were corrected in place: policy pointer at the top of each, the flash removed from every live claim, the Polar fee break-even corrected to ~$900/mo, and the projections flagged as written against a dead model.
This commit is contained in:
+26
-30
@@ -1,40 +1,36 @@
|
||||
# HANDOFF — 2026-10-30 (scoreboard built to the polling policy; two wiring items left)
|
||||
# HANDOFF — 2026-10-31 (commercial plan settled in docs; next code unit is the entitlement seam)
|
||||
|
||||
## State
|
||||
|
||||
- Branch `main`. The audio dock is shipped and pushed. **The scoreboard is built** in ten units,
|
||||
local commits only: tallies (`Models/ScoreboardState`, session-scoped), lifecycle
|
||||
(`ScoreboardRecorder`: start clears, End freezes), layout (`Models/ScoreboardLayout`: coupled
|
||||
edges resolved at render time, shrink-then-wrap, never scroll), renderer
|
||||
(`Services/Compositor/ScoreboardRenderer`: plate + text colour + opacity + 14–24px, cached),
|
||||
tile selection (`Models/ScoreboardTiles`, opt-in, money per currency), the cached presenter, the
|
||||
layer itself (`SourceType.Scoreboard`, strip geometry, resolver case), the properties panel
|
||||
(eight fact switches + colours + plate + size, theme widgets only), the Analytics scope
|
||||
(`yt-analytics.readonly`, incremental, `HasAnalyticsScope`), and the polling policy
|
||||
(`Models/ScoreboardCadence`).
|
||||
- Fixed on the way: the chat feed now reaches the tallies (the recorder had no caller).
|
||||
- Branch `main`. Pushed through `c72b61b`; **`90d5140` + this commit are local only** (push at a
|
||||
checkpoint, not per commit).
|
||||
- **Commercial plan is settled and written down**: freemium (free = whole app at a 921,600 px /
|
||||
30 fps profile, stream **and** recording), Pro = $10/mo or $400 lifetime through **Polar** (key
|
||||
bound to the buyer's YouTube channel ID via a claim page, 2 activations, 7–14 day lease), one
|
||||
portable zip + **winget** as the primary channel, **Steam and the Store parked**, Azure Trusted
|
||||
Signing, Founding 200 promo. New single source of truth: [`docs/ai/commercial.md`](docs/ai/commercial.md).
|
||||
- Docs consolidated this unit: `product.md` (money section → pointer), `decisions.md` (banners +
|
||||
history), `index.md`, `ai.md`, `README.md`, `Distribution.md` (rewritten to release mechanics,
|
||||
736 → 120 lines), `TASKS.md` (five new rows), `capture.md` (GPL line falsified by the proprietary
|
||||
EULA), plus gitignored `MARCOM.md` / `MONETIZATION.md`. Site repo: `AGENTS.md`, `README.md`,
|
||||
`build-this.md`.
|
||||
- Scoreboard: built, unchanged from the last handoff (HTTP providers + goal inputs still missing).
|
||||
|
||||
## Next action
|
||||
|
||||
1. **HTTP providers** behind the cadence policy: watch hours (`reports.query`,
|
||||
`metrics=estimatedMinutesWatched`) and subscriber count (`channels.list?mine=true&part=statistics`),
|
||||
both behind an interface with a fake for tests, plus the timer that calls
|
||||
`ScoreboardCadence.Due(...)` and writes the values into `ScoreboardTiles`.
|
||||
2. **Goal-target inputs** in the properties panel (the tiles read `SubscriberGoal`/`WatchHourGoal`
|
||||
but nothing sets them yet).
|
||||
3. **On-screen check** of the strip: one line at 24px, plate/opacity, and the edge swap against the
|
||||
social bar.
|
||||
1. **Entitlement seam** — `IEntitlement` over Polar activate/validate/deactivate, channel-ID match,
|
||||
2 activations, lease + offline grace, `--activate LCYT-…` CLI, fail closed. One Good Dog test.
|
||||
2. Then the **OutputProfile gate** (one profile consumed by both the stream encoder and the
|
||||
recorder; greyed tier entries; never downgrade mid-stream), the licence UI, and system messages.
|
||||
3. Scoreboard HTTP providers + goal inputs whenever the creator queues them.
|
||||
|
||||
## Landmines
|
||||
|
||||
- **Not every hex is a theme colour.** `BorderColorPalette` and the chat/socials config defaults
|
||||
are USER DATA — they keep their literals. On-stream graphics now come from `OutputPalette`,
|
||||
never from a literal or from app resources (the pump thread must not touch them).
|
||||
- A red settings control is a bug now — red means live/record.
|
||||
- **Nothing may gate a feature.** The only paid delta is the output profile. `ShowBrandFlash` stays
|
||||
`false` and entitlement must never enable it.
|
||||
- **The site publishes the model, never the numbers** — and its CTA ("Coming soon to the Microsoft
|
||||
Store") stays until the creator says the download exists.
|
||||
- **No self-updater, by policy**: `winget upgrade` + a version banner. `StorePackagingTests` asserts it.
|
||||
- Pre-existing: the **full test suite hangs** (WASAPI teardown). Per-class runs are the way.
|
||||
|
||||
## Pending
|
||||
|
||||
- **Launch and eyeball**: fonts render (pack-URI resolution cannot be proven in the test host),
|
||||
the blue accent, blue-staged / red-live tiles, and mono telemetry.
|
||||
- Vertical-tier camera check; webcam-cadence take in the failing geometry.
|
||||
- Not every hex is a theme colour — `BorderColorPalette`/chat defaults are USER DATA; on-stream
|
||||
graphics come from `OutputPalette`. A red settings control is still a bug.
|
||||
|
||||
Reference in New Issue
Block a user