The price change
Lifetime $49.99 → $79.99, discounted lifetime $34.95 → $55.99. No new products and no app release — ✓ products_config.json carries no prices, ✓ no hardcoded prices in any .dart/.ts/.tsx, and ✓ the 30% discount works by swapping to a separate product id, not a coupon.
| # | Where | Change |
|---|---|---|
| A0 | scripts/_payments.js | Do this first. usdPriceByType() medians price across all history per SKU. After the change full_purchase2025 holds both $49.99 and $79.99 and the median drifts between them for months — every per-SKU revenue figure stays plausible and becomes wrong. Bucket by (SKU, month). |
| A1 | Google Play Console | full_purchase2025 → $79.99 · discount_full_purchase2025 → $55.99. Set regional pricing deliberately, not auto-convert. ✓ Not doable by API — inappproducts 403s with “migrate to the new publishing API” and no v3 replacement resolves. Console UI only. |
| A2 | App Store Connect | Same two. ✓ No API path at all — no .p8 key exists. |
| A3 | Stripe — last | Same two. ✓ Live state confirmed: $49.99 / $34.95. A Stripe Price amount cannot be edited — create a new Price on the existing Product (so metadata.shortcode carries over), create a new Payment Link, then deactivate the old link. |
Deactivate the old Stripe links. /payments/get (routes/payments.js:1433) lists every active payment link with no dedupe by shortcode — leave the old one up and the app shows two lifetime products at two prices.
Stripe goes last. Doing it before the stores leaves web at $79.99 while both apps still charge $49.99.
✓ Safe: nothing hardcodes a buy.stripe.com URL anywhere — links are fetched live, so replacing them is transparent.
Every website CTA now routes through applyDiscount?code=WEBPROMO1, which serves the discount SKU. So web-acquired buyers pay $55.99, not $79.99 — +12%, not +60%. That path is tiny today (21 of 1,551 purchases last quarter) but the CTA fix was built to grow it. $55.99 is the price most new web users will actually see.
While in the consoles: grab the three real prices. The corrected store copy is blocked on exactly those, and the iOS listing is wrong today — it advertises a 6-month plan that does not exist, and “10 profiles free” when the real limit is 5.
Ship what is already fixed
Zero new code. ✓ Verified against git on 2026-09-11.
| # | Repo | Action |
|---|---|---|
| B0 | all — do this first | ISSUE-149 — 24 finished Flutter commits have been sitting unreleased since 2026-09-09. Verified three ways: git log 8583ffaf..HEAD = 24 commits, pubspec.yaml still reads 11.0.0+300100090 with no bump since the 09-08 cut, and zero store release emails in 4 days. All pushed to origin/new-ui — nothing is lost, purely a cadence gap. The fixes for ~13 tracked issues are stranded, including ISSUE-129 (Android purchases hitting the payment call with an empty userId — 261 Google Play orders in 30 days landing unattributed). Cut a release. Single highest-value item on this page. |
| B1 | astromatrix-public | Commit + push app/home/terms/ and Footer.tsx. The Terms URL printed in every store description 404s, which blocks the ASO submission under Apple guideline 3.1.2. Push auto-deploys — run npm run build locally first and check git log origin/master..HEAD. |
| B2 | astromatrix-flutter ( new-ui) | Commit products_config.json + gated_content_widget.dart — the paywall sells two things the free tier already has. Note the shipping branch is new-ui, 116 commits ahead of master. |
| B3 | astromatrix-nodejs | 2 unpushed commits on master. |
| B4 | infra, not code | Set the autosupport key in production config and update the scheduled job in the same change, or live autosupport breaks. |
Before working any “committed but not deployed” item from the fire docs, check git first. Two such claims were already in origin/master despite the docs saying otherwise.
Quick code fixes
ISSUE-150 (C5), ISSUE-151 (C7) and ISSUE-152 (the pricing work in §A) are now numbered issues in the master fire report and reflected into SUPPORT-fire-FLUTTER.md / SUPPORT-fire-NODE.md. The rest were already tracked — C1 and C3 in SUPPORT-ux-experience-issues.md, C4 as ISSUE-132, C8/C9 in that same doc §8.
⚠ Removed since the first draft: the /matrix-report dead route. It was fixed 2026-09-11 (9b7dd49e) and the app-wide sweep came back clean — zero unregistered navigation targets remain. Do not re-file it.
| # | Where | Item |
|---|---|---|
| C1 | ✓ home_buttons .dart:149-161 | The home button says the wrong word. Home says “Weekly”; reporters search for “transits”. ⚠ The obvious fix — carrying the "Weekly (Transits)" label onto the button — is NOT buildable: home labels are maxLines: 1 + TextOverflow.ellipsis at fontSizeSM, so it renders as “Weekly (Tra…”, and every label runs through getLang() so German and Turkish truncate sooner. Existing labels sit at 6–13 characters — that is the budget. The real fix: merge Weekly + Monthly into ONE “Transits” button (8 chars), opening the Matrix with the Weekly/Monthly/All chips from C3 visible on arrival. Fewer words, the exact word three reporters used, one freed grid slot. Ship with C3. |
| C3 | ✓ matrix_service .dart:898 | Surface the Matrix lens names as on-screen chips instead of dropdown contents — nobody opens a dropdown to discover a feature. All 7 labels already exist in getCategoryMenuOptions(): All Aspects · Foundation (Birth) · All Transits · Weekly (Transits) · Monthly (Transits) · Growth (Progressed) · Soul (Draconic). Presentation only — no new strings, no astrology. |
| C4 | § ISSUE-132 | Relationship Matrix silently auto-pairs a second profile and persists it app-wide. Two bail-out guards are written && mounted, so an unmounted widget skips the return as well as the pop. Small and self-contained; not verified on a device. |
| C5 | § notification_ router.dart | ISSUE-150 — notification deep-links dead-end on Home. The router is wrapped in a try/catch that only debugPrints, so any throw silently leaves the user where they were — on a cold launch, the home screen. Fall back to a real destination and record the failure. Reported by a lifetime customer; this is an engagement channel already being paid for. |
| C6 | ✓ routes/journals .js:520 | The post-reading tarot comment is discarded — writes comment: '', ignoring the request body. One line — but it is deliberate legacy parity, not a bug, so confirm intent first. (The docs cite :451, which is the wrong line.) |
| C7 | § chart_screen.dart | ISSUE-151 (UNVERIFIED — check the widget first) — the synastry wheel sits 6th in the chart dropdown, behind Progressed at 3rd — a practitioner chart outranking the one chart a normal person came for. Reorder. Not verified. |
| C8 | § astro_helper .dart:1375 | South Node not visible on some surface. It already exists end to end — engine body index 27, glyph present, already a row in the transit aspect table. Scope is only “find which surface drops it”. Narrower than originally filed. |
| C9 | § chart_constraints .dart | Add a showOuterPlanets flag for Uranus/Neptune/Pluto. 13 per-body visibility flags already exist; classical and outer planets currently share one blunt showPlanets. Leave nodes and angles on the existing flag — traditional practice uses the nodes, so a naive “classical vs post-Saturn” split hides exactly what the requester wants kept. |
Deliberately excluded — not quick
| Item | Why not |
|---|---|
| Event store | Design work, not a one-liner. Prometheus is aggregate-only; a uid label across 460,694 accounts is a cardinality explosion. A design already exists in user-knowledge-audit-2026-09.md §I3. |
| Relationship transits | An engine change, despite the docs saying otherwise. CalcChart() has two chart slots and applyComposite() consumes both. Needs a decision on which of three techniques first. |
| Link the store / give reports a route | The highest-value item on the roadmap, but real work — not a quick win. |
| Raising the annual subscription price | Subscriptions carry Apple/Google consent and notification requirements for existing subscribers. Lifetime is a non-consumable and has none. Separate job. |
| Anything needing App Store Connect automation | ✓ No .p8 key exists. Provisioning one (Admin role) unblocks four things at once: Apple price automation, the 62% of revenue never reconciled against Apple’s ledger, iOS review monitoring, and iOS install data. |