Audit · 11 September 2026 · AstroMatrix
Every claim in the ASO package checked against the shipped Flutter client, the Go ephemeris engine, the live paywall and the live web. The copy craft is sound and all 32 character counts are exact — what stops it are three disclosure problems and a second store-copy package written the same day that says something different. Two items have since been fixed in code, and one of them reversed a finding: the six-languages claim was never the overclaim.
The site is up and the privacy page is fine — only Terms is gone, in every casing and extension I tried. Apple expects a working Terms / EULA link in the metadata of an auto-renewable subscription app (3.1.2), and a reviewer clicking a dead legal line is an avoidable rejection. Lifetime buyers following it today get the 404 too.
Cause: a page lost in the Next.js rewrite. astromatrix.org is served by astromatrix-public, which has app/home/privacy and no app/home/terms — exactly the difference between the 200 and the 404. The page did exist: the legacy .NET site served 325 lines of Terms of Service from Views/Home/Terms.cshtml. It was never carried over.
Fixed app/home/terms/page.tsx restores the legacy text verbatim, mirroring the privacy page; Footer.tsx gains the Terms link it never had. Dev server returns 200 with the full text, tsc and eslint clean, left uncommitted.
Still yours The restored text is dated — Facebook/Twitter sign-in, "Astromatrix, Inc.", California venue, numbering that skips sections 4 and 9, and no auto-renewal clause at all. Restoring what was published is an ops fix; drafting new subscription terms is a lawyer's job, so nothing was rewritten.
The promo text reads "Now with downloadable PDF reports and complete interpretations in six languages", with no qualifier on either half. PDF export is genuinely Premium-only — it is the one true capability among the app's gates, and three of the ten upgrade prompts in the app are PDF prompts titled "Unlock PDF Reports".
The other package got this right: "read in six languages … unlock every chart type with a single lifetime purchase." This one dropped the qualifier.
Fix Re-attach the purchase to the PDF half. The languages half needs no qualifier — see below.
This started as the other half of Blocker 2: both the upgrade dialog and every paid plan sell reading in your language as Premium, so the unqualified store claim looked like an overclaim. Tracing the gate instead of reading the copy reversed it. Language is not gated anywhere:
Chart types are not gated either — content_gating_service.dart never references ChartType. So both "Reports in your language" and "Every chart type — synastry, composite…" are benefits the free tier already has, which is precisely what rule 4 of the positioning doc forbids: "A benefit the free tier also has teaches users the paywall is noise." That is the same rule that deleted "Created by real astrologers" that morning, from the same file.
Fixed Both bullets removed — from the upgrade dialog and from all four paid plans (products_config.json → 1.2.0), leaving PDF export, unlimited saves and no-ads. flutter analyze shows only the three pre-existing info lints; the product-config tests pass 10/10.
Yours The paid plans now show three bullets instead of five. Whether a fourth gets added is a copy call the positioning doc owns — nothing was invented to fill the gap.
Filed concurrently by another session while this audit was running: /ai/generateDataReport — the Matrix AI report, the app's main AI surface — has no language plumbing on either side, so it comes back in English regardless of the user's setting. A paying Spanish subscriber has been asking about it for roughly three weeks.
So the listing's line is true of the interpretation corpus and not true of that one surface. It is a content bug to fix rather than a qualifier to add to the copy, but it should be fixed before the line is submitted in six languages.
The description's only account of the paywall is "Premium adds downloadable PDF reports and an ad-free experience." The real gates, verified in code:
The other package states the free tier outright and deliberately reframed Premium away from ad-removal, recording that a wrong free-tier limit on a store page is "a refund and review risk." This package reverts to the framing that was just fixed. The English description is 2,978 characters — 3,440 with Apple's terms block — so there are roughly 560 characters spare to say it properly.
Fix Add the free-tier line and the full Premium list. The room is already there.
It covers renewal mechanics, charging and cancellation, but never names what is being sold. Apple's 3.1.2 metadata requirement includes the length and the price of each subscription. The live SKUs are quarterly_subscription, yearly_subscription2025 and full_purchase2025; the other package carries all three with [PRICE] placeholders and a note to pull real values from App Store Connect rather than guess.
Fix Add durations and prices. Pull them from App Store Connect — the figures are not in either repo.
It carries its own verification list, and is governed by the copy rules in positioning-and-paywall-copy.md §4. The two agree on both app names and on the positioning line — and disagree on every field that is actually being changed:
| Field | This package | The other package |
|---|---|---|
| Apple subtitle | Birth Chart & Compatibility (27) | Your full chart, not your sign (30) |
| Apple keywords | mainstream-led — astrology, zodiac, daily, love, rising, moon | practitioner-led — synastry, composite, progressed, draconic, relocation, vedic |
| Play short desc. | Birth charts, daily horoscopes, synastry, composite charts, transits and tarot. | Your full birth chart: synastry, composite, transits, tarot. Not just your sign. |
| Promo text | PDF + six languages, unqualified | PDF + six languages, tied to the lifetime purchase |
These are genuinely different bets — broad-demand acquisition against depth-led differentiation — and §15's test plan only measures one challenger against the live control, so running it as written never compares them to each other.
Fix Pick the winner per field, or run the two short descriptions against each other rather than against the incumbent.
The copy promises "chemistry, support, connection, growth and challenges" — five. The app has four:
"Growth & Transformation" is a sub-facet under Evolution; "Challenges" is an aspect trait, not a facet. English, Spanish, Italian, Portuguese and Turkish all say growth + challenges; French alone says évolution — so the one locale that names the real facet is the odd one out.
Fix "chemistry, support, connection and evolution" in all six, or drop the enumeration.
So the visible ceiling is 27, and a fresh install shows about 20. The number is defensible as a content claim and the other package uses it too — but a screenshot captioned 29 PLANETS, ASTEROIDS & CHART POINTS cannot be produced honestly: you would enable seven toggles and still be two short.
Fix Keep the claim in prose; re-caption the screenshot to what the frame actually shows.
Entitlement demotion is still HIGH and unvalidated — the client fix is committed and merged, the bypass-health metric has not moved, and the fire report attributes about 42 of 43 "ads" complaints to this bug rather than to ads. Paying users get silently demoted and start seeing ads.
§15's primary metric (first-time acquirers per visitor) is install-side and unaffected. Its guardrails — rating trend and retained installer rate — are not: they are measuring the entitlement bug. Sending more traffic to a listing whose paid promise is "no ads, ever" while it is live amplifies the existing review pattern.
Fix Run the Play experiments — they're safe. Gate the three keyword-targeted listings on a post-11.0.0 read of _bypass_health.js rate 30.
Verified in the internal analytics docs: synastry at 23% of chart views, the add-partner flow predicting a relationship reading at 59.7% against 18.1%, and transits as ~40% of interpretation reading. I could not source "birth charts are 29% of chart views", "transit charts are 21% of chart views" or "relationship interpretations are about 17% of reading". The §1 list also mixes chart-view share with reading share without naming either denominator — which five-user-modes.md calls out by name as the comparison to stop making.
The competitive work calls it the best-retained feature in the app — 17 visits per account, 15.4% one-visit, the best figure anywhere in the product. It earns a frame more than the sixth slot's generic "Advanced astrology in one app" does.
Mongolian carries 9,160 content rows against 18k–125k for the six, and the other package excludes it deliberately for that reason — correct. Worth knowing the picker still offers it (settings_sections.dart:412), so a Mongolian speaker can select a language the listing doesn't claim and the corpus doesn't really cover.
Note the host is astromatrix.app, while the listing's support and terms lines use astromatrix.org — different hosts, both real. Confirm Play Console actually exposes a deep-link field on a keyword-targeted custom listing before the plan leans on it.
Every count the package states is correct to the character, including the accented and Turkish fields. Three sit exactly on the limit — Apple keywords at 100, and the Play and CSL 3 short descriptions at 80 — so any later edit to those has to be a like-for-like swap.
| Repo | File | Change |
|---|---|---|
| astromatrix-public | app/home/terms/page.tsx | New. Legacy Terms of Service restored verbatim, privacy-page structure |
| astromatrix-public | app/components/Footer.tsx | Adds the Terms link the footer never had |
| astromatrix-flutter | gated_content_widget.dart | Drops “Reports in your language”; comment records every gate checked |
| astromatrix-flutter | products_config.json | Four paid plans lose both false bullets; version → 1.2.0 |
| astromatrix-nodejs | SUPPORT-fire-*.md | ISSUE-144 and ISSUE-147 filed, indexed and mirrored |
Nothing was committed or pushed. ISSUE-144 (Terms URL) and ISSUE-147 (paywall bullets) are filed in SUPPORT-fire-manager-report-2026-08-26.md and mirrored into SUPPORT-fire-FLUTTER.md with ranked actions. My issue was renumbered 145 → 147 mid-session because another session claimed 145 and 146 while this was being written.
Everything above was checked against astromatrix-flutter at 9116d402 on new-ui, astromatrix-engine/types.go, astromatrix-public on Next 16, the product docs in astromatrix-nodejs, and live HTTP requests to astromatrix.org. Prices were not verified — they are in App Store Connect and nowhere in the repos.