Skip to content

Pricing scheme inventory + fallback billing copy fixes - #12

Open
formed2forge wants to merge 7 commits into
mainfrom
pricing-scheme-inventory-clean
Open

Pricing scheme inventory + fallback billing copy fixes#12
formed2forge wants to merge 7 commits into
mainfrom
pricing-scheme-inventory-clean

Conversation

@formed2forge

Copy link
Copy Markdown
Owner

Summary

Prep work ahead of the new pricing scheme: an inventory of every pricing/plan
touchpoint in the repo, plus three small hardening fixes surfaced by that
inventory that don't require any final price/tier decision.

  • docs: docs/agents/pricing-scheme-touchpoint-inventory.md — maps every
    place mobile/desktop/web/backend/docs display, hardcode, or test plan
    names, prices, or feature-limit numbers, distinguishing live-read values
    (no update needed when prices change) from hardcoded duplicates. Also
    lists surfaces confirmed absent from this repo entirely (App Store/Play
    listings, ad copy, socials) that need a separate non-code audit. Builds on
    the existing docs/agents/plan-source-of-truth.md / plan-catalog.md
    catalog-migration design rather than duplicating it.
  • fix(desktop): macOS and Windows each shipped two different fallback
    question-count numbers (200 vs 100) for the same "unlimited" (Neo) plan
    when the backend catalog response omits subtitle/description
    confirmed 200 is correct against backend/config/plan_catalog.json. Fixed
    both, made the macOS fallback-copy helpers static so they're testable
    without constructing a full SettingsContentView, and extracted the
    hardcoded "$49/mo" Operator price in each platform's deprecation-banner
    fallback into one named constant per platform.
  • refactor(web): deduped web/app's two independent copies of the
    generic paid-plan feature bullet list into web/app/src/lib/planFeatures.ts.

Test plan

  • macOS: xcrun swift build -c debug --package-path desktop/macos/Desktop
    clean build. Added SettingsContentViewBillingFallbackTests.swift
    (2 tests) asserting the fallback subtitle/description agree on the
    question count; ran via xcrun swift test --filter SettingsContentViewBillingFallbackTests — both pass.
  • Windows: added a regression test to billing.test.ts for the same
    invariant; ran the full suite via vitest run — 68/68 pass.
  • web/app: bun run typecheck (tsc --noEmit) — clean.
  • make preflight — all checks pass except web-app-checks, which fails on
    Sidebar.test.tsx/StartupModals.test.tsx (localStorage undefined in
    the test environment). Verified pre-existing and unrelated: reproduces
    identically on an unmodified origin/main checkout in a throwaway
    worktree, in files this PR never touches. Likely an artifact of running
    via bun locally (this machine has no pnpm/node, only bun) rather
    than the pinned CI toolchain.

Product invariants affected

none

Failure class (fixes)

Failure-Class: none

formed2forge and others added 7 commits August 29, 2026 01:02
Maps every place across mobile/desktop/web/backend/docs where plan
names, prices, or feature-limit numbers are displayed, hardcoded, or
tested, ahead of the new pricing scheme. Distinguishes live-read values
(no update needed) from hardcoded duplicates, and lists surfaces
(App Store/Play listings, ad copy, socials) confirmed absent from this
repo that need a separate non-code audit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…rator deprecation price

macOS and Windows each fell back to two different question-count numbers
(200 vs 100) for the same "unlimited" (Neo) plan when the backend catalog
response omitted subtitle/description — verified against the canonical
200/month allocation in backend/config/plan_catalog.json. Fixed both, and
made BillingHelpers' fallback copy functions static so they're directly
testable without constructing a full SettingsContentView.

Also extracted the hardcoded "$49/mo" in each platform's deprecation-banner
fallback into a single named constant (operatorDeprecationFallbackPrice /
OPERATOR_DEPRECATION_FALLBACK_PRICE), so a future Operator price change is
a one-line edit instead of a search through prose strings. These fallbacks
only render when the API omits deprecation_message, so no pricing decision
is required for this change.

Failure-Class: none

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
SettingsPage.tsx and PlansSheet.tsx each hardcoded their own identical
copy of the generic paid-plan feature bullets. Extracted to
web/app/src/lib/planFeatures.ts so the two surfaces can't silently drift
when the copy changes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Backticked references need to be plain repo paths so the checker can
verify they resolve; line numbers now sit outside the backticks as
plain text, and a few component-relative shorthand paths (Desktop/...,
src/renderer/..., src/app/...) are now full repo-relative paths.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Six code files this PR modifies (BillingHelpers.swift, AccountBilling.swift,
billing.ts, PlanUsageTab.tsx, billing.test.ts, SettingsPage.tsx,
PlansSheet.tsx) had line numbers cited in the inventory doc; the edits
shifted several of them. Recomputed each against the actual pre/post-edit
blobs. Also updated the three findings this PR fixes (100-vs-200
inconsistency, hardcoded $49/mo, duplicated defaultFeatures) from open
findings to "fixed in this PR", and removed the now-redundant duplicate
defaultFeatures row in favor of pointing at the new shared file — the
"Suggested next steps" section was telling the reader to fix things this
same PR already fixes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
SettingsContentView's extension methods (planSubtitle/planDescription)
inherit MainActor isolation from the view type, so calling the newly
static helpers from a plain XCTestCase synchronously failed to compile
under Xcode 16.4's actor-isolation checking. Verified via
`xcrun swift test --filter SettingsContentViewBillingFallbackTests`,
which failed before this fix and passes after (2/2).

Failure-Class: none

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant