-
Notifications
You must be signed in to change notification settings - Fork 0
docs: reconcile the free-tier variance chart with the 2.1.0 ship state #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
964dbf3
2da5a40
182f8df
05a0ed1
ac07ca4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,30 @@ Produced 2026-08-15 by reading all three sources directly. Sources: | |
| - **Store copy** ... the 2.1.0 full description and release notes drafted | ||
| 2026-08-15 in `/projects/Marketing/nine-lives-marketing/store-listing.md`. | ||
|
|
||
| ## Status, 2026-08-24 | ||
|
|
||
| **Every listing conflict in this document is closed IN THE APPROVED 2.1.0 | ||
| COPY.** The store copy was rewritten on 2026-08-24 and re-approved the same | ||
| day, and the paid-feature list in it was copied verbatim from | ||
| `UNLOCK_BENEFITS` in `ui/unlock/UnlockScreen.kt` rather than written | ||
| alongside it. | ||
|
|
||
| **That copy is a one-time reconciliation, not a mechanism, and this document | ||
| should not have called it structural.** `UNLOCK_BENEFITS` is a private Kotlin | ||
| value in this repo, the canonical listing lives in the marketing repo, and | ||
| nothing generates one from the other or checks them against each other. The | ||
| next edit to either side can re-drift exactly as before. A real check is | ||
| filed separately. | ||
|
|
||
| **The listing LIVE on Play is a different question and it is still open.** It | ||
| carries the pre-rewrite text until the flip-week refresh replaces it | ||
| wholesale, so the grouping conflict in row 9 is closed in the approved copy | ||
| and open on the live listing. Where this document says a row is resolved, it | ||
| means the approved copy unless it says otherwise. | ||
|
|
||
| Read section 1 for what changed, and 1b for the class of error this chart was | ||
| never built to catch and therefore missed. | ||
|
|
||
| ## The headline | ||
|
|
||
| **The code is not drifting from the plan. The plan is drifting from itself.** | ||
|
|
@@ -33,37 +57,70 @@ Legend: **Y** gated / specified · **n** not mentioned · **--** not applicable | |
| | 1 | Downloads capped at one offline book | **Y** | **Y** | **Y** `DownloadSlotStore.slotApplies` | states it | **aligned** | | ||
| | 2 | Playback speed pinned 1.0x | **Y** | **Y** | **Y** `FreeTier.allowsSpeed` | states it | **aligned** | | ||
| | 3 | Sleep timer, 30-minute preset only | **Y** | **Y** | **Y** `FreeTier.SLEEP_TIMER_MINUTES` | states it | **aligned** | | ||
| | 4 | Sleep timer motion grace, shake-to-reset, rewind-on-expire | n | **Y** | **Y** normalized off | n | summary gap · **listing silent** | | ||
| | 5 | Auto-rewind on resume | n | **Y** | **Y** `autoRewindEnabled=false` | n | summary gap · **listing silent** | | ||
| | 6 | Equalizer and volume boost | n | **Y** | **Y** normalized off, clamped in `PlaybackManager` | n | summary gap · **listing silent** | | ||
| | 7 | Silence skipping | n | **Y** | **Y** `skipSilenceEnabled=false` | **advertises it as new, no gate note** | **listing conflict** | | ||
| | 8 | Sorts: 8 of 11 gated | n | **Y** | **Y** `FreeTier.SORT_MODES` | n | summary gap · **listing silent** | | ||
| | 9 | Grouping: series, author, genre | n | **Y** | **Y** `VIEW_MODES = {ALL}` | n | summary gap · **listing silent** | | ||
| | 10 | Archive Shelf browsing and manual restore | n | **Y** (auto-restore and deletion stay free) | **Y** `LibraryScreen` tab lock | **describes it as a feature, no gate note** | **listing conflict** | | ||
| | 11 | Nightwatch Dossier and share card | n | **Y** | **30-day window FREE**, longer periods gated, as of 2026-08-16 | **describes it as a feature, no gate note** | **resolved in code**, listing still needs the period note | | ||
| | 12 | Themes | n | **Y** NOIR free, other three gated | **NOIR + BRIGHT free** as of 2026-08-15 | n | **code ahead of plan, deliberate** | | ||
| | 13 | Advanced server settings (self-signed cert, TOFU) | n | **Y** "gated in the UI" | **NO GATE** ... bare `Switch`, no `GatedControl` | n | **code gap** | | ||
| | 14 | Android Auto browse gating and command grants | n | **Y** | **NO GATE** ... zero entitlement refs in `PlaybackService`, `MediaBrowseTree`, `RemoteMediaAccessPolicy` | n | **code gap** | | ||
| | 4 | Sleep timer motion grace, shake-to-reset, rewind-on-expire | n | **Y** | **Y** normalized off | covered by "full sleep timer control" in the unlock list | summary gap · **listing aligned 2026-08-24** | | ||
| | 5 | Auto-rewind on resume | n | **Y** | **Y** `autoRewindEnabled=false` | named in the unlock list | summary gap · **listing aligned 2026-08-24** | | ||
| | 6 | Equalizer and volume boost | n | **Y** | **Y** normalized off, clamped in `PlaybackManager` | named in the unlock list | summary gap · **listing aligned 2026-08-24** | | ||
| | 7 | Silence skipping | n | **Y** | **Y** `skipSilenceEnabled=false` | named in the unlock list, and the release notes now mark it "(new)" inside the unlock sentence | **resolved 2026-08-24** | | ||
| | 8 | Sorts: 8 of 11 gated | n | **Y** | **all 11 FREE** as of 2026-08-15, `FreeTier.SORT_MODES` = every entry, mechanism retained | n | **code ahead of plan, deliberate** | | ||
| | 9 | Grouping: series, author, genre | n | **Y** | **Y** `VIEW_MODES = {ALL}` | named in the unlock list | gated, ships gated · **approved copy resolved 2026-08-24, LIVE listing still open until the refresh** | | ||
| | 10 | Archive Shelf browsing and manual restore | n | **Y** (auto-restore and deletion stay free) | **Y** `LibraryScreen` tab lock | **names browsing as a paid unlock feature**, and points at the full list | **resolved 2026-08-24** | | ||
| | 11 | Nightwatch Dossier and share card | n | **Y** | **30-day window FREE**, longer periods gated, as of 2026-08-16 | **names the free window as permanent and the longer ones as paid** | **resolved 2026-08-24** | | ||
| | 12 | Themes | n | **Y** NOIR free, other three gated | **NOIR + BRIGHT free** as of 2026-08-15 | AMOLED and Candlelight named in the unlock list | **code ahead of plan, deliberate** | | ||
| | 13 | Advanced server settings (self-signed cert, TOFU) | n | **Y** "gated in the UI" | **NO GATE** ... bare `Switch`, no `GatedControl` | n | ships ungated in 2.1.0, deliberate · see reconciliation | | ||
| | 14 | Android Auto browse gating and command grants | n | **Y** | **NO GATE** ... zero entitlement refs in `PlaybackService`, `MediaBrowseTree`, `RemoteMediaAccessPolicy` | n | **never gated, decided 2026-08-16** · see reconciliation | | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Row 14 now records Android Auto as a locked decision to remain ungated, but the current Useful? React with 👍 / 👎. |
||
| | 15 | Local folder cap | dropped 2026-08-15 | marked superseded | never built | correctly silent | **aligned** | | ||
| | 16 | ABS library cap | dropped 2026-08-15 | marked superseded | never built | correctly silent | **aligned** | | ||
|
|
||
| ## What actually needs a decision | ||
|
|
||
| ### 1. The three listing conflicts (rows 7, 10, 11) | ||
| ### 1. The listing conflicts (rows 7, 10, 11) ... RESOLVED 2026-08-24 | ||
|
|
||
| This was the row set that produces angry reviews, because the listing promised | ||
| something the app then charges for. It was also the cheapest to fix, being copy | ||
| rather than code. | ||
|
|
||
| The recommendation below was to move the gated features into the unlock | ||
| paragraph rather than tag each mention. Jeff took a version of that on | ||
| 2026-08-24 and went further: | ||
|
|
||
| This is the row set that produces angry reviews, because the listing promises | ||
| something the app then charges for. It is also the cheapest to fix: it is copy, | ||
| not code. | ||
| - `BUILT FOR LONG LISTENS` is now free features only, and ends with a line | ||
| pointing at the paid list. | ||
| - The Archive Shelf and Dossier sections each name their gate inline and point | ||
| at the same list. | ||
| - The old `FREE AND UNLOCKED` section split into `WHAT FREE GETS` and | ||
| `THE PAID UNLOCK, IN FULL`, so the pointers land on a header that says what | ||
| it is. | ||
| - The paid list was copied **verbatim** from `UNLOCK_BENEFITS` in | ||
| `ui/unlock/UnlockScreen.kt`, whose own comment says it exists so the unlock | ||
| screen and the store listing cannot drift. It had drifted anyway, which is | ||
| the point: a comment asking people to remember is not a mechanism. Copying | ||
| beats paraphrasing and it makes rows 4 through 11 true today, but it is a | ||
| one-time reconciliation and the next edit to either artifact can undo it. | ||
| The mechanical check is issue #13, not this paste. See the status block. | ||
|
|
||
| - **Silence skipping** is in the 2.1.0 release notes as "Also new: silence | ||
| skipping" with no indication it is an unlock feature. A free user reads the | ||
| release note, updates, and finds a locked toggle. | ||
| - **Archive Shelf** and **Nightwatch Dossier** each get their own section in the | ||
| full description, written as capabilities the app has. Both are gated. | ||
| Approved copy is the paste package in the marketing repo, re-approved | ||
| 2026-08-24 09:57 after three review passes. | ||
|
|
||
| Fix: either add the gate to each mention, or move all three into the unlock | ||
| paragraph. Recommend the second, since it makes the unlock paragraph a real | ||
| value proposition instead of three limits phrased as subtractions. | ||
| ### 1b. What this chart was never built to catch | ||
|
|
||
| Every row here is a **free-versus-paid variance**: is a gated thing described as | ||
| free. That framing is correct and it found five real conflicts. It is also | ||
| blind to a second class of error, and the 2026-08-24 audit found three of those | ||
| in copy this document had already reviewed: | ||
|
|
||
| - `Widget and full media-notification controls` promised a home-screen widget | ||
| that **does not exist in this repo at all**. No `AppWidgetProvider`, no | ||
| Glance, no appwidget receiver in the manifest, no widget XML. The | ||
| notification half is real, which is how the bullet survived a read. | ||
| - `Sleep timer that fades out` ... it does not fade. `SleepTimerManager` pauses | ||
| immediately at zero when motion sensing is off, which is the free path. | ||
| - `Run both modes side by side ... in one app` ... `AppMode` is an exclusive | ||
| enum read as either/or in `LibraryViewModel` and `SettingsViewModel`. You | ||
| configure both and switch. | ||
|
|
||
| None of those are variance. A feature that does not exist has no tier. So when | ||
| this chart is next run, run a second pass that asks a different question of | ||
| every claim: **not "is this gated" but "is this true."** | ||
|
|
||
| ### 2. The Dossier strategy conflict (row 11) ... DECIDED 2026-08-16 | ||
|
|
||
|
|
@@ -95,25 +152,33 @@ Rejected alternatives, for the record: share card without the Dossier screen | |
| (splits one feature into two half-features), and leaving it gated while dropping | ||
| H1 (throws away the only user-distribution mechanism in the plan). | ||
|
|
||
| Still open: the listing copy needs a period note, since the full description | ||
| describes the Dossier without saying the long windows are an unlock feature. | ||
| ~~Still open: the listing copy needs a period note, since the full description | ||
| describes the Dossier without saying the long windows are an unlock feature.~~ | ||
| **CLOSED 2026-08-24.** The approved copy now says the thirty-day window is | ||
| free and never expires, and that longer retrospectives are a paid unlock | ||
| feature, with a pointer to the full list. No copy work remains on this row. | ||
|
|
||
| Recommend the first. The card is an acquisition asset, and gating your own | ||
| acquisition asset behind a purchase is backwards. | ||
|
|
||
| ### 3. The two code gaps (rows 13, 14) | ||
| ### 3. The one open gate question (row 13), and one that is decided (row 14) | ||
|
|
||
| Both are planned gates that were never built. Neither is a correctness bug and | ||
| Both were planned gates that were never built. Neither is a correctness bug and | ||
| neither leaks entitlement in the wrong direction, so free users currently get | ||
| *more* than intended. | ||
|
|
||
| - **Advanced server settings**: this one is arguably better left ungated. | ||
| Self-signed cert trust is how a self-hoster connects to their own box at all. | ||
| Gating it is close to gating access to a user's own data, which is the line | ||
| the paywall philosophy says not to cross. | ||
| - **Android Auto browse gating**: worth deciding deliberately rather than by | ||
| omission, especially since PRs #88, #90, and #92 all touched Auto and none | ||
| added it. | ||
| - **Advanced server settings (row 13), still open.** This one is arguably | ||
| better left ungated. Self-signed cert trust is how a self-hoster connects to | ||
| their own box at all. Gating it is close to gating access to a user's own | ||
| data, which is the line the paywall philosophy says not to cross. No formal | ||
| lock, and the code ships ungated with the listing silent, so nothing is | ||
| promised either way. | ||
| - **~~Android Auto browse gating~~ (row 14), DECIDED 2026-08-16: never gated.** | ||
| This section previously called it a gap and asked for a deliberate decision. | ||
| That decision was made and Android Auto is free, so the absence of | ||
| entitlement references in `PlaybackService`, `MediaBrowseTree` and | ||
| `RemoteMediaAccessPolicy` is the intended end state rather than an omission. | ||
| Kept here rather than deleted because the old framing was cited elsewhere. | ||
|
|
||
| ### 4. The plan doc sentence | ||
|
|
||
|
|
@@ -143,3 +208,53 @@ the default, not the first free entry`). | |
| The plan doc's locked decision on themes and the `FreeTier` lore line ("the | ||
| vault is what you get, lighting is a privilege you buy") are both now superseded | ||
| and need updating in place. | ||
|
|
||
| ## Reconciliation, 2026-08-23 | ||
|
|
||
| Written the night 2.1.0 (212) was finalized for production, to close the rows | ||
| still marked open above. Facts and decisions, with provenance: | ||
|
|
||
| - **Row 14, Android Auto: never gated. Locked decision, 2026-08-16.** Auto | ||
| ships free in the category king's Basic tier, and a gate here would punish | ||
| the exact commute use case the listing sells. The absence of entitlement | ||
| references in `PlaybackService`, `MediaBrowseTree`, and | ||
| `RemoteMediaAccessPolicy` is the intended end state, not a gap. The old | ||
| "code gap" verdict is superseded. One live artifact still contradicts the | ||
| decision: the listing currently on Play (mirrored in `store-listing.md` | ||
| at the repo root, PLAYBACK section) sells the unlock as adding Android | ||
| Auto. The approved 2.1.0 copy drops that claim, and the flip-week listing | ||
| refresh replaces the live text wholesale. Until then the live listing | ||
| oversells the unlock in Auto's favor ... known, accepted, and it dies at | ||
| the refresh. When the refresh publishes, re-sync the root mirror and | ||
| `reference/play-store-listing.txt` in the marketing package from the | ||
| approved paste source, per the mirror's own header rule. | ||
| - **Row 13, advanced server settings: ships ungated in 2.1.0.** The standing | ||
| recommendation (self-signed cert trust is how a self-hoster reaches their | ||
| own box, and gating it sits next door to gating access to a user's own | ||
| data) has not been formally locked, but the code ships ungated and the | ||
| listing is silent, so nothing is promised either way. If a future release | ||
| wants this gate, that is a new decision, not a regression fix. | ||
| - **Row 9, grouping: gated, and 2.1.0 ships it gated.** Whether it stays | ||
| gated long-term is formally still open, marked low priority 2026-08-16. | ||
| As of the 2026-08-24 rewrite the approved copy names grouping in the paid | ||
| list, as "Series, author and genre grouping", which is the honest place | ||
| for it. Earlier drafts of this section said the copy never mentioned | ||
| grouping at all, which was true of the 08-23 version and is not any more. | ||
| The listing live today is another story: its LIBRARY section advertises | ||
| "grouping by series, author, or genre" with no gate note | ||
| (`store-listing.md` mirror, same section). That line is one more thing the | ||
| flip-week refresh retires, and it stays a listing conflict, not a closed | ||
| row, until the refresh is live. | ||
|
Comment on lines
+246
to
+247
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Before the flip-week refresh, the live listing still advertises grouping without a gate note, and this sentence explicitly says that conflict remains open until the refresh. That contradicts the new top-level claim that every listing conflict is closed and row 9's closed verdict. Distinguish the approved-copy status from the live-listing status so the document has one answer for whether this conflict is currently open. Useful? React with 👍 / 👎. |
||
| - **Row 8, sorts: all 11 free since 2026-08-15.** Chart cell updated. The | ||
| gate mechanism is retained deliberately ... a lever not pulled is not a | ||
| lever removed. | ||
| - **Rows 4, 5, 6, 7, 10, 11, the listing gaps and conflicts: CLOSED | ||
| 2026-08-24.** The 2026-08-23 approval left every one of them open, because | ||
| that approval had only ever checked the free-tier claims and not the | ||
| feature claims. Jeff reopened it on 08-24 and the rewrite closed the set | ||
| rather than patching the worst case. See section 1 above for what changed | ||
| and why the paid list is now copied from `UNLOCK_BENEFITS` instead of | ||
| written alongside it. | ||
| - **The 30-day trial is not in 2.1.0.** Decided 2026-08-16, cut 2026-08-22: | ||
| v212 ships without it and the trial moves to 2.2 as a release beat. No | ||
| listing or doc text may mention a trial until it ships. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This row now marks the Dossier listing conflict resolved because the rewritten copy names the free and paid windows, but lines 145–146 still say the listing needs a period note and remains open. Anyone using this as the decision record is therefore given contradictory instructions about whether copy work is still required; update or mark that older action as superseded.
Useful? React with 👍 / 👎.