The ask
A mechanical check that the store listing's paid-feature list still matches
UNLOCK_BENEFITS.
Why, and why a comment is not enough
UNLOCK_BENEFITS in ui/unlock/UnlockScreen.kt carries this comment:
Kept in one list so the unlock screen and the store listing cannot drift.
They drifted anyway. By 2026-08-23 the approved 2.1.0 store copy described six
unlock-only features as free (silence skipping, the equalizer, volume boost, the
motion-sensing sleep timer, shake-to-reset, auto-rewind on resume) plus the
Archive Shelf, which had its own section headline. Nobody noticed until a manual
audit on 08-24.
The 08-24 fix was to copy the constant verbatim into the listing. That made them
match. It did not make them stay matched, and docs/free-tier-variance.md
briefly claimed otherwise before a review caught it. A one-time copy is a
reconciliation. The next edit to either side re-drifts, and the only thing
standing in the way is somebody remembering a comment in a Kotlin file.
Shape
A script, run before any listing change is staged:
- Parse the
UNLOCK_BENEFITS list out of UnlockScreen.kt. It is a plain
listOf(...) of string literals, so this is a small parse and does not need
the Kotlin toolchain.
- Parse the bullets under
THE PAID UNLOCK, IN FULL out of the approved paste
package.
- Diff. Non-zero exit and a readable report on any mismatch, in either
direction.
Where it lives
The marketing repo, not this one. The canonical listing lives there, this repo
is public, and an app-repo test cannot read a path outside its own tree. The
script reads the app source by path, which works because both repos sit on the
same machine.
Sibling to tools/compose_shots.py, which already owns the "verify the assets
before they go to Console" job. Same idea, applied to words instead of pixels.
Requirements
- Give it a deliberate-failure canary, the way
compose_shots.py --canary
has one. A gate nobody has watched fail is not a proven gate. Mutate one
string, confirm the check reports it, restore.
- Compare on normalized text, since the listing wraps at ~72 characters for
human reading and the Kotlin strings do not. Whitespace and wrapping
differences are not drift.
- The two are allowed to word things differently in principle. If they should
be, the check needs an explicit mapping file rather than a fuzzy match, so
that any intentional divergence is written down instead of tolerated
silently.
Origin
Codex review on PR #6, 2026-08-24, correctly pushing back on this document
calling a manual copy a structural fix. The finding was right and the honest
answer is to build the mechanism rather than reword the claim, so the wording
was fixed there and the mechanism is filed here.
Post-2.1.0. Nothing here blocks the release or the price flip.
The ask
A mechanical check that the store listing's paid-feature list still matches
UNLOCK_BENEFITS.Why, and why a comment is not enough
UNLOCK_BENEFITSinui/unlock/UnlockScreen.ktcarries this comment:They drifted anyway. By 2026-08-23 the approved 2.1.0 store copy described six
unlock-only features as free (silence skipping, the equalizer, volume boost, the
motion-sensing sleep timer, shake-to-reset, auto-rewind on resume) plus the
Archive Shelf, which had its own section headline. Nobody noticed until a manual
audit on 08-24.
The 08-24 fix was to copy the constant verbatim into the listing. That made them
match. It did not make them stay matched, and
docs/free-tier-variance.mdbriefly claimed otherwise before a review caught it. A one-time copy is a
reconciliation. The next edit to either side re-drifts, and the only thing
standing in the way is somebody remembering a comment in a Kotlin file.
Shape
A script, run before any listing change is staged:
UNLOCK_BENEFITSlist out ofUnlockScreen.kt. It is a plainlistOf(...)of string literals, so this is a small parse and does not needthe Kotlin toolchain.
THE PAID UNLOCK, IN FULLout of the approved pastepackage.
direction.
Where it lives
The marketing repo, not this one. The canonical listing lives there, this repo
is public, and an app-repo test cannot read a path outside its own tree. The
script reads the app source by path, which works because both repos sit on the
same machine.
Sibling to
tools/compose_shots.py, which already owns the "verify the assetsbefore they go to Console" job. Same idea, applied to words instead of pixels.
Requirements
compose_shots.py --canaryhas one. A gate nobody has watched fail is not a proven gate. Mutate one
string, confirm the check reports it, restore.
human reading and the Kotlin strings do not. Whitespace and wrapping
differences are not drift.
be, the check needs an explicit mapping file rather than a fuzzy match, so
that any intentional divergence is written down instead of tolerated
silently.
Origin
Codex review on PR #6, 2026-08-24, correctly pushing back on this document
calling a manual copy a structural fix. The finding was right and the honest
answer is to build the mechanism rather than reword the claim, so the wording
was fixed there and the mechanism is filed here.
Post-2.1.0. Nothing here blocks the release or the price flip.