§A · Front matter
The seven sources
All seven were published to claude.ai within about 36 hours of each other. Three are orientation maps covering substantially the same ground, which is why §B exists.
| # | Document | Kind | Updated | What it owns |
|---|---|---|---|---|
| I | AstroMatrix Ground Truth | Orientation | 09-11 | The roadmap with a live status column checked against the working tree. Strongest on what is built and connected to nothing, and on decisions waiting on a call. |
| II | AstroMatrix Orientation | Orientation | 09-11 | The fullest of the three. Carries the measured pricing analysis, the engine correction, the verified/retracted number tables and nine standing traps. Where it disagrees with I or III, it usually wins. |
| III | AstroMatrix Orientation Map | Orientation | 09-11 | The shortest route in. Best on which repo holds what and the three-lane doc taxonomy. Its roadmap detail is superseded by II. |
| IV | The AstroMatrix Playbook | Strategy | 09-11 | Product, competitive position, pricing shape, marketing channels and the advocate base. The only document covering marketing and the moat. |
| V | The Matrix Question | Product brief | 09-11 | One screen, in depth: why the Matrix works, where people fall out of it, and five moves. The only document with journey-level detail. |
| VI | What AstroMatrix Knows | Audit | 09-11 | Code-level audit of everything the product stores or could derive about a person. The only document on the knowledge and memory layer, and it found a security hole. |
| VII | AstroMatrix Quick Wins | Task list | 09-11 | Everything plausibly under half a day, with the verification state of each marked. The most recent of the seven — its issue numbers run highest. |
§B then §C. Between them they carry every conflict and every quotable number in the set. After that, VII if you want to do something today, IV if you want the argument, VI if you want the thing nobody else looked at.
§B · Front matter
Where these documents disagree
Ten places where two or more of the seven make incompatible claims. This is the section that did not exist in any source document, and it is the reason the set was worth consolidating: each of these would otherwise be re-derived, wrongly, by whoever read the wrong doc first.
The rule applied throughout: a claim checked against source code or a live console beats a claim carried forward from another document, regardless of which document is newer or longer.
Not an engine build. “The engine already treats composite as natal and the 3-slot orb system already covers transit-to-composite.” The Playbook calls it “three menu entries, not a rebuild.”
It is a real engine change, and both supporting points above are misreadings of the source. CalcChart() has exactly two chart slots, dPos and TransitPos. applyComposite() consumes both to midpoint person 1 and person 2 into dPos — line 869 only labels the result like a natal chart. Transits to a composite need three charts at once, which the two-slot model cannot express in one call. And Orbs[] is [0]=natal, [1]=synastry/composite, [2]=transit — three orb settings for three chart types, not a transit slot for composite.
The correction wins — it is the only version that cites engine source. Three options exist and they are not equivalent: (1) transits to the composite, what the plan implies, needing a third slot or a two-pass API — real engine work; (2) a Davison chart, the midpoint in time and space, which has a genuine date/time/location and runs through the existing engine as an ordinary natal chart with syn=2 transits — no engine change, and not implemented anywhere; (3) each partner's own transits side by side, which works today with two existing calls but answers a different question. Name which one Horizon 1.1 is before estimating it. The underlying asymmetry is real either way: the self matrix has six lenses, the relationship matrix has none.
“Reprice to 3–4× annual, or retire it.” 41–45% of purchases; the single largest structural cap on LTV; stop stacking −30% on it.
Raise to $79.99. Do not retire. The last price rise already ran the experiment — lifetime went $29.99 → $49.99 in Oct 2025, and Jun–Aug year-over-year (which controls for the Nov–May slump that hit every SKU) gives units −6%, realized price +78%, revenue +68%. On the break-even switch-rate table, at $79.99 retiring can never pay against annual ×1.5 and needs 98% switching at ×2.04. Lifetime money also arrives today; annual money arrives over 2+ years conditional on a renewal depth nobody has measured per SKU.
II supersedes. Raising the price is itself what settles the keep/retire question. And the genuinely mispriced SKU is annual, not lifetime: at $39.99 it is dominated on both sides — quarterly is $19.99/quarter = $79.96/year, and lifetime is $49.99, so annual is both the cheapest single year and cheaper than two years of quarterly. Quarterly outsells the new annual 480 : 126 and is displayOrder: 1, shown first. Raise annual and reorder before adding any new SKU — and do not add a fourth subscription; the ladder is already incoherent. Execution steps are in VII §A.
“Decide the language gate before pushing ISSUE-39. Shipping ungated permanently forecloses charging for localisation.”
12b9965 is already in origin/master, not pending. That window has closed or is closing.
Already decided in code: ISSUE-147 pulled language off the paywall, and e6c07df5 now sends the user's language with AI report requests. Effectively free by default.
Decided free, by default rather than by choice. Gating it now would be a withdrawal rather than an addition, which is a materially worse thing to ship. Worth confirming deliberately on paper rather than inheriting. Separately note ISSUE-146: /ai/generateDataReport — the main AI surface — is still English-only for every user regardless of setting, on both sides of the call. That is narrower than ISSUE-122 and not covered by it. See VI §04.
There is a live numbering collision: the Flutter doc's “ISSUE-144 — Terms of Use URL 404s” is the master's ISSUE-148, and the master's actual ISSUE-144 is a different issue entirely about account deletion leaving personal data in three collections.
Fixed while assembling, 09-11. Renumbered to ISSUE-148 across the master and the derived docs, because ISSUE-144 is anchored in routes/user.js and in commit 1823e6e.
Its uncommitted-work table labels the Terms fix “ISSUE-144” — carrying the pre-renumber label.
Terms of Use 404 = ISSUE-148. Account-deletion data remnants = ISSUE-144. Any doc still using 144 for the Terms fix predates the renumber.
III: 33 open / 53 in progress / 60 resolved, next free № 149, “147 issues.” II: 36 open / 54 fixed-not-live / 65 closed, “~147 issues.” I: “129 numbered issues, running to ISSUE-148.” VII: ISSUE-149, 150, 151 and 152 now filed.
These were written hours apart against a register that concurrent sessions were actively editing. The highest number known to this set is ISSUE-152. The standing rule holds and is the real takeaway: re-read the master register's highest number immediately before filing a new row — a stale read causes a silent collision, which is exactly what produced B4.
Free tier gets 10 saved charts, 10 saved reports, and 5 people.
The iOS store listing advertises “10 profiles free” when the real limit is 5 — and separately advertises a 6-month plan that does not exist.
“Any chart-count threshold above 5 is mechanical, not predictive — the free limit is 5 charts.”
People: 5. Saved charts and saved reports: 10. The store listing conflates them and is wrong in the user's favour, which is the direction that generates refund requests. Fix it while in the consoles for B2 — and note the analytical consequence: because the free gate is 5 people, any conversion threshold quoted above 5 charts is an artifact of the paywall, not a discovered behavioural boundary.
24 commits committed and unreleased since the 09-08 build cut. 2 files uncommitted. “The single highest-value action on this page is a version bump.”
ISSUE-149. 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 already pushed to origin/new-ui — nothing is lost, it is purely a cadence gap. The fixes for ~13 tracked issues are stranded, including ISSUE-129, where Android purchases hit the payment call with an empty userId: 261 Google Play orders in 30 days landing unattributed.
III: “4 commits unpushed.” II: “4 unpushed commits.” VII, latest: “2 unpushed commits on master.”
Two different problems wearing one label. Flutter is pushed but unreleased — the fix is to cut a build, not to push. Node had unpushed commits, and the count drifted 4 → 2 as sessions pushed. The operative rule, stated in both II and VII: check git, not the docs — two separate “committed but not pushed” claims in the fire register were already in origin/master. And remember the chain: committed ≠ pushed ≠ deployed ≠ released ≠ in a build a real user has.
“TOP PRIORITY, committed but not pushed.”
It is in origin/master (e90aa02). Pushed still is not deployed on the Node box — but the register's framing is stale and should not be driving the priority order.
The code exists on the mainline; whether it is deployed on the Node box was never confirmed. That is a one-command check against restart timestamps and it should be done rather than assumed. The general rule stands: verify any “unpushed” claim in the fire docs against git before ranking it.
$0.85 blended LTV per new account · ≥$1.78 paid-ads bar · $2.40 twelve-month value at $0.20 ARPMAU.
$0.85 ($0.50 IAP + ~$0.32 ads) is “correct for what it measures — a blended average over overwhelmingly organic installs. Not the paid-ads bar.” The paid-ads bar is ≥$1.78 net per install, IAP only, measured on the acquired PRIMO10 cohort — 643 installs, 46 payments, converting at 7.15% against 1.6–1.8% blended, because acquired users clicked an astrology ad on purpose.
“Leave the ads campaign alone until day 3 gives an honest CPI against the $0.85 bar” — using the figure it had just ruled out for exactly this purpose.
$0.85 answers “what is a new organic account worth?” ≥$1.78 answers “what must a paid install clear?” and is the one to judge a campaign against. $2.40 is IV's twelve-month projection from ARPMAU, useful only for the order-of-magnitude comparison against a $2–4 iOS CPI range. Judging the campaign against $0.85 sets the bar roughly 2× too low. Recompute with scripts/_ads-roi.js, which re-derives it from the ledger every run.
Of roughly 49 interface complaints, exactly one named the Matrix as too complex. About 25 said a version of “I cannot find a thing I already know exists.” Four separate confirmed cases of a live feature being reported as deleted.
“Too complex” and “too sparse” are both right: six users want more on screen, one articulate user wants less. Solve it with a density mode — never by picking a side.
Three documents reached this independently, which makes it the best-supported conclusion here. Do not simplify the Matrix — it is already the simplification, and removing depth answers one user while damaging twenty-five. The fix is almost always the step between two screens, not either screen. And the standing rule that prevents the next occurrence: never convert a destination into a state without leaving a signpost for one release.
§C · Front matter
The numbers, merged
Three of the seven documents carried a partial copy of this table. Here it is once, merged, with every retraction in one place. Before quoting any level, re-derive it: node scripts/_analytics.js verify runs about 2.5 minutes against live production with no cached stats.
A usage-stats sample covering 16.6% of accounts, skewed toward engaged users, was quoted as if it were fleet levels. It inflates conversion ~2.8× and undercounts volume 14–44×. Anything dated before 2026-09-07 carries that error. Crucially: relative lifts in those documents still hold — only the levels moved.
The one variable that explains the revenue
Not which features someone likes, and not how often they visit. How many birth charts their account holds. Visit frequency spans only ~2.5×, and behavioural segment membership collapses to ~1.00× — no effect at all — once charts held is controlled for. So there are not five user types with different values. There is one axis.
Ever-paid rate by number of charts held
460,694 accounts · verified against production 2026-09-07
A 34× spread, perfectly monotonic — and it points at two different jobs. The 1 → 2 chart step (1.58% → 5.92%) is the largest lever over the largest population: 300,710 accounts who have not yet done the thing that predicts paying. Separately, ~11,500 accounts hold 8+ charts and have never paid — demonstrated demand, unconverted. That second one is a conversion play, not an ARPU play; an earlier brief called the segment near-saturated, and that was the inflated number.
Segment names used across the docs: Solo (1) · Curious (2–3) · Enthusiast (4–7) · Collector (8–15) · Astrologer (16+).
Verified — safe to quote
| Accounts | 460,694 · 5.37% ever paid · 3.73% paying now |
| Scale | 87,467 MAU (Android 43,983 / iOS 43,128) · $0.20 ARPMAU · ≈$17,195/mo, roughly $17–20K/mo with renewals · ads ≈29% of revenue · 18-minute sessions, 86.6% engagement · 4.6★ displayed on both stores |
| The decline | Android active device installs 137,256 → 88,659 (Dec 2022 → Aug 2026) — about 35% over four years, steady rather than accelerating. Android-only; no verified iOS equivalent exists |
| Blended LTV | ≈$0.85 per new account ($0.50 IAP + ~$0.32 ads). A blended average over overwhelmingly organic installs. Not the paid-ads bar — see B9 |
| Paid-ads bar | ≥$1.78 net per install, IAP only — PRIMO10 cohort, 643 installs / 46 payments, converting 7.15% against 1.6–1.8% blended. Before price work-through, renewals or ad revenue. Re-derive with scripts/_ads-roi.js |
| Store payout | 79.8% — not 70% |
| Payer value is rising | avg net $17.34 → $25.10 (+44.7%) as the $49.99 and quarterly SKUs displace legacy ones |
| Purchase timing | Median days from first open to purchase: 0. 63% buy same-day, 82% within seven days, and 57% buy before they have even registered. Time-to-value is a revenue mechanic, not a satisfaction one |
| Purchase mix | 45% of all purchases are a one-time lifetime unlock; one SKU at $49.99 is 41% of all sales. Average realised price $34.86; the 30% discount vehicle takes lifetime as low as $34.95 |
| Age pays | A 65+ user is worth 8.8× an 18–24 user. Ages 45+ are 18.9% of accounts and 32.2% of revenue |
| Tarot | 6.26M journal entries, 4.54M carrying a typed question · ~638k completed readings/yr at 67% completion · 8.5% of free-text questions are romance · 17 visits per account — the best-retained feature |
| Relationships | 155,361 accounts (34.6%) hold another person's chart · 72.2% of multi-chart accounts have never opened the relationship screen · 93.2% of share-link arrivals bounce under 10s · adding a second chart moves reading rate 20.7% → 51.2% |
| Discoverability | reports 7.8% reach and no route at all · harmonizer 2.8%, one link · store 0.2%, one link · synastry 1.5% · composite 0.3% · the Guide 1.3% · Progressed 1.3% · /chartupcoming 0.2% |
| Matrix screen | 68.8% of tracked accounts reach it — more than any screen except Home · 29.1% never return, second lowest in the app · retention is a slide not a cliff: 94% reach a 2nd visit, 78% a 5th, 41% a 20th |
| Activation threshold | Reading one interpretation moves activity 2.7% → 17.9% — the sharpest threshold measured |
| Website | ~89–98K monthly users · 54,872 search clicks per 28 days · CTA click rate ~3.2% · 90%+ never return · zero site → app conversion tracking |
| Content & cost | ~59,700 interpretation documents across 7 languages · total AI spend $80.79/month, of which only 25% is the product · non-English users are ~14% combined, split across 29 languages |
| Competitor pricing | The four major subscription competitors average ~$230/year — Nebula $415, Sanctuary $240, CHANI $144, The Pattern $120. The $49.99 lifetime is 22% of that in year one and 7% over three |
Retracted — do not quote these again
| Wrong, and still circulating | Verified against production |
|---|---|
| 15.02% ever paid | 5.37% |
| 448,892 accounts | 460,694 |
| Solo: 35,430 at 4.69% | 300,710 at 1.58% |
| “Tarot converts worst” | False — an aggregation artifact. Within any collection size, tarot users convert better |
| Practitioner segment “63% converted, an ARPU play” | ~11,500 unconverted — a conversion play |
| Synastry ≈23% of chart views | 6.5% |
| “316 documents carry user text” | 4,544,139 stored tarot questions |
| Website CTA rate 0.21% | ~3.2% |
| “$585K/yr” revenue | ~$15.5K/mo new + renewals ≈ $20K/mo |
| GA4's “50% crash in H2 2024” | Measurement artifact — use Play Console device counts |
| Chani's “42% retention / 3.1× premium” | Fabricated. Never cite |
| “There is no Meta pixel on the site” | It exists — it was firing into an orphaned pixel |
| Any chart-count threshold above 5 | Mechanical, not predictive — the free limit is 5 people |
profiles and users are not the same thingprofiles = accounts (531,848). users = charts and people (1.24M). They are not interchangeable and they have already been mixed up in published figures. Related: sample levels are not fleet levels, and usage of an undiscoverable feature is not a measure of demand — reports have no route, the harmonizer has one link, astrocartography's route is not registered, and 8,352 journal prompts were built and never surfaced. Low numbers on any of those mean nothing until they are reachable.
AstroMatrix Ground Truth
One bet, three document lanes, one roadmap. Everything scattered across those sessions is one of three lanes describing the same company from a different angle.
You are not missing a roadmap. You have one, and it is buried.
The roadmap is competitive-position-2026-09.md §7 — Horizon 0 / 1 / 2. Nothing else is a roadmap. Everything else is either evidence feeding into it, or a defect register tracking what breaks underneath it.
The bet
Relationships as structural distribution. Committed for the quarter. Can AstroMatrix generate users from users?
The constraint
Nothing can be graded. Per-user events do not exist, so no shipped fix in the register can be proven to have worked.
The drift
24 commits are unreleased. Two days of fixes sit in the repo with no version bump, in nobody's hands.
State of the code, checked against the working tree
| Measure | Value | Note |
|---|---|---|
| Mainline | new-ui | In sync with origin. master is unused — and new-ui is 116 commits ahead of it. Do not reason about Flutter state from master. |
| Live on both stores | 11.0.0 | Build 300100090, released 09-09. iOS and Android on the same build. |
| Committed, unreleased | 24 | Commits since the build was cut on 09-08. No version bump since. |
| Uncommitted | 2 files | Paywall copy + products_config.json at 1.2.0. |
| Fire register | 129 | Numbered issues, running to ISSUE-148. |
The aspect-grid fixes, the Matrix auto-pairing fix, the purchase-queue drain, four telemetry repairs, the AI report language fix and the app_version metric tagging are all committed and in nobody's hands. They were written on 09-10 and 09-11; the last release was cut on 09-08. Cutting a build converts two days of finished work into shipped work, and it is the prerequisite for measuring any of it.
The quarter's bet: relationships as distribution, not a feature
The diagnosis behind it: Android active installs fell 137,256 → 88,659 over four years — steady, structural, not a bad release. In that same period product work was real and effective, but no feature ever had “create a new user” as its primary job. This quarter tries the one mechanism that has never been tried.
| Element | Decided |
|---|---|
| Question | Can AstroMatrix generate users from users? |
| Loop | Add a partner → generate a live relationship reading (strongest connection now, current tension, what's changing, next window) → send it → recipient opens with no install required → recipient is the subject, not a bystander → they create an account to unlock their side. |
| Audience | State 2 — accounts holding 2+ charts who have never opened the relationship screen (72.2% of them), and Self-Explorers. Explicitly not Newcomers, who already carry too much setup burden. |
| Hard constraint | Do not gate the first reading behind the partner installing. If the loop fails it must be because the mechanism does not work, not because users resented a gate. |
| The test | Does attributable acquisition from this loop exceed enough of the existing loss rate to change the net curve? Not engagement, not shares — new users who appeared because existing users brought them. |
| Ships alongside | The funnel instrumentation. Named as a prerequisite, not a follow-on. |
Explicitly deprioritised for the quarter — not for lack of value, but because running them in parallel makes the one question unanswerable: more chart types · more generic interpretations · broad Ask-AstroMatrix development · journalling expansion · meditation/harmoniser expansion · a generic social feed · a large UI redesign · extensive ASO iteration · growth-content campaigns · offline/local calculation.
The roadmap, with what has actually moved
Horizon 0 — this month, no new systems
Entirely additive or price-side. Carries no grandfathering exposure. There is no case for scoping a new paywall limit until 0.1–0.4 have shipped and been measured — until then nobody knows what the product converts at when its entrance is findable and its paywall describes the real product.
| # | Action | Status | Where it stands |
|---|---|---|---|
| 0.1 | Replace the store's plan copy | Unreleased | Committed a16c5d50. The “50GB storage / 24-7 support / API access” copy was also found to be dead code that never rendered — belt and braces. |
| 0.2 | Fix the lifetime SKU | Your call | Untouched. 41% of purchases and the single largest structural cap on LTV. superseded — see B2 |
| 0.3 | Delete the false upgrade bullets | In working tree | Two removed earlier; ISSUE-147 removed two more (“Every chart type”, “Reports in your language”) after tracing every gate and finding neither is gated anywhere. Paid plans now show three bullets. |
| 0.4 | Link the store and reports | Open | The highest-value unstarted item in Horizon 0. The /premium dead-end behind all 12 gates was fixed 09-07, but the store still has one inbound link in the whole app (0.2% reach) and reports have no route at all — modal-only inside 6 screens, 7.8% reach, plateaued after launch. ISSUE-76. |
| 0.5 | Decide the language gate | Decided in code | Settled by default: ISSUE-147 pulled language off the paywall and e6c07df5 now sends the user's language with AI report requests. Worth confirming on paper — and note ISSUE-146, where the Matrix AI report is still English-only for everyone. |
Horizon 1 — the quarter, relationships and time-to-value
1.1–1.3 are the core bet — make the relationship layer timed, legible and not only romantic. Everything else in this horizon distributes it.
| # | Action | Status | Why it's on the list |
|---|---|---|---|
| 1.1 | Relationship transits — “what's happening between you two this week” | Not started | The angle nobody else can match. Turns a one-shot verdict into a daily reason to return. this doc's “not an engine build” claim is wrong — see B1 |
| 1.2 | A one-word verdict on every pair | Not started | We compute more and conclude less. A dashboard cannot be retold; a single word gets screenshotted. |
| 1.3 | Relationship types — friend, family, colleague, partner | Not started | 9 of the 12 facets are already relationship-neutral. The astrology is non-romantic already; only the packaging is romantic. |
| 1.4 | Invite-from-chart flow | Not started | 155,361 accounts already hold another person's chart. The highest-ROI person-to-person moment in the product, and our largest dormant asset. |
| 1.5 | Shareable pair card — “Us”, not “me” | Not started | The competitors' actual unit of growth. We share only self-charts today. |
| 1.6 | Fix the cold-arrival landing | Not started | 93.2% of share-link visitors to relationshipfacets bounce in under 10 seconds. Do this before 1.5 sends more traffic at it. |
| 1.7 | Public-figures database | Not started | The famous fields exist with zero rows and the isFamous param is read but unused. Cheapest viral surface in the category — a content job, not engineering. |
| 1.8 | Promote the synastry wheel | Not started | It sits sixth in the chart dropdown, below Progressed. A practitioner chart outranks the one chart a normal person came for. |
| 1.9 | Surface comparisons to the 72.2% who never make one | Not started | 1 → 2 charts moves conversion 1.58% → 5.92%. Needs pre-filtering and a cap: O(n²), 79,800 pairs at 400 charts. |
| 1.10 | Resolve MessagesScreen — foundation or delete | Not started | Don't build a second messaging surface beside an unreachable one. ISSUE-47. |
| 1.11 | Contextual reading button on the relationship surface | Not started | The cheapest link in the app. Pastes both names in as the tarot question — which is the question users already type by hand. |
| 1.12 | Refresh the video library | Not started | Owned channel, stale content. Production not engineering — and it cheaply tests whether presented content lands before committing to an audio hire. |
| 1.13 | Per-user event measurement | Half shipped | Blocks all pricing work. The cheap half shipped 09-11 (fcd34d34 — app_version on every metric at the single choke point). The expensive half is real design work. ISSUE-77 / ISSUE-142. |
Horizon 2 — the year, new models
| # | Action | Status | Note |
|---|---|---|---|
| 2.1 | The Coach / Oracle session layer | Specced | Full spec written across three docs, nothing built. The answer to the only real competitive weakness. Total AI spend today is $80.79/month, of which only 25% is the product — real headroom. |
| 2.2 | The Oracle as a shareable weekly card | Specced | Turns the Coach into the social object the product currently lacks. |
| 2.3 | Practitioner marketplace | Concept | Power users take paid consultations, we take a cut. Built on 4,011 Astrologers at 54% conversion plus the P2P substrate. The largest revenue model in the category, and the only one competitors cannot replicate. |
| 2.4 | Audio | Gated | The stack is already shipped; the content needs a hire. Do not schedule until 1.7 has told us whether presented content lands at all. |
Fix the revenue shape and ship additive value before subtracting anything. The product is already perceived as harder to use than a cheaper competitor, and the internal rating moved 3.78 → 3.44 on a change that added something. Adding limits to that product risks making the limit, rather than the packaging, the reason to leave.
Built, working, and connected to nothing
Repeatedly, strategy passes proposed things that turned out to already exist. These are code-verified as present and unreachable — the common shape is that the computation exists and the distribution does not.
| Asset | Verified state | Missing piece |
|---|---|---|
| Universal sky-event engine | Void-of-Course, Out-of-Bounds and moon phase are computed with no chart, no location and no user — and a batch generator already writes 4 years of them to JSON. The live API returns post-ready sentences today. | Any publishing or broadcast layer. Nothing reads the generated files. |
| Event-triggered broadcast push | The client receiver is fully wired on a MoonPhases topic channel. | The sender. Absent from all three repos. |
| Big Three rarity | ~165k charts with stored Sun/Moon/Ascendant including house. | Any feature that uses it. |
| Astrocartography | 894 lines, substantially complete — and advertised on the website as shipped. | One timezone bug misplacing lines 100+ km, plus un-gating the flag. |
| The onboarding answer | Step 2 asks “what do you need answers about?”, requires an answer to advance, and discards it in the same frame — never persisted, not even as an analytics label, since it shipped in Dec 2025. | One field. Roughly a day's work, on a self-declared life priority collected from 100% of new users at peak intent. |
| A behavioural store | users.stats accumulates per-screen visit counts and dwell times for every user, syncs to Mongo, and survives reinstall. | Nothing has ever read it. No surface, no report, no AI prompt. |
| Three native home-screen widgets | Moon Phase, Sabian Symbol and Planetary Hours — built twice, once for each platform. | Unknown whether they are promoted anywhere. Their Android refresh was also broken for months by the install-referrer bug. |
Decisions nobody else can make
Each has the analysis finished and is waiting on a call, not on engineering.
Cut a build ships 24 commits
Two days of finished fixes, including the instrumentation that makes every later question answerable, sitting unreleased behind an unchanged version number.
The lifetime SKU — reprice or retire Horizon 0.2
41% of purchases and the largest structural cap on lifetime value. No existing user loses anything either way. See B2 — the measured answer is $79.99, do not retire.
The ISSUE-61 remediation rollout 48,576 accounts
Birth locations silently mismatched from their town labels. The id-based fix is live and stops new profiles hitting it, but the rollout — auto-correct 39,839 high-confidence records, ask the other 8,737 to re-enter — was designed and never started. The largest data-integrity exposure on the register by account count, frozen until someone says go.
The paywall's fourth bullet ISSUE-147
Removing the two ungated claims leaves paid plans showing three bullets instead of five. Nothing was invented to fill the gap. Whether a fourth gets added is a copy decision, owned by
positioning-and-paywall-copy.md.The Matrix report size gate ISSUE-136
The Summary + All Aspects report is gated as an oversized payload, but the gated path allows 50MB against an estimated ~1MB report. The 100kb limit that is real belongs to a route that button never calls. Needs the original dev to say whether it was ever measured — otherwise re-justify it as a render-memory gate and move it to the PDF path.
Confirm the language decision on paper Horizon 0.5
Already made in code. That forecloses charging for localisation, which is a real strategic choice worth making deliberately rather than inheriting.
The tracking system itself has drifted
Part of why this feels scattered is that the doc meant to prevent scattering is out of date. Verified against both files:
- The Flutter fire doc is missing real, open, Flutter-owned issues — 91, 94, 104, 111, 138, 142 and 146 are all in the master register and absent from the derived view. Its own banner flagged four of them on 09-10 and they are still missing.
- There is a numbering collision — see B4.
- It is being hand-patched against its own stated rule that it must be regenerated rather than edited — which is how both problems above arose.
- The two biggest absentees are not small. ISSUE-138: the 2,921-test suite never runs anywhere, there is no CI test or analyse step,
masterdeploys straight to production ungated, and the suite is currently red with 82 failures. ISSUE-142: release attribution — whose absence is why no fix in the register can be proven to have worked.
The fix is a regeneration from the master register, not another patch. Until that happens, check the master's index before concluding any Flutter issue is not tracked.
↑ Back to index topAstroMatrix Orientation
The fullest of the three orientation documents, and the one that does the most correcting. It collates and reconciles the scattered docs, introduces no new findings, and points every claim at the doc that owns it.
The company, in one paragraph
AstroMatrix is a profitable, deeply-built astrology product — roughly $17–20K/month, 87,467 MAU, 4.6★ — whose Android install base has declined about 35% over four years, steadily. Three years of real product depth work were effective at what they targeted, but no feature ever had user-creation as its explicit job.
Separately, the loyal base that remains is badly monetised (5.37% ever paid, $0.20 ARPMAU) — largely because the paths to pay were structurally broken, not because demand is absent. Those are two distinct problems: a growth-engine failure and a packaging failure. They have separate fixes and should not be collapsed into one narrative.
The single company question this quarter: can AstroMatrix generate users from users?
The five lanes
There is no single project here. There are five parallel lanes that each acquired their own tracker — which is the actual source of the “it's all over the place” feeling.
| Lane | Where it lives | Its tracker | State |
|---|---|---|---|
| A · Product & growth strategy | flutter/docs/, nodejs/docs/product/ | FEATURE-LIST.md | converged |
| B · Engineering fires | nodejs/docs/SUPPORT-fire-* | master fire report | ~147 issues |
| C · Website & SEO | astromatrix-public/ | memory: SEO audit | audit done, queue open |
| D · Paid ads & economics | astromatrix-public/docs/ | SESSION-HANDOFF | day-3 read pending |
| E · Infra, DR & migration | nodejs/docs/ | INFRA-*, MIGRATION-* | on a 3–6 mo clock |
Lanes A and B are the same product at two altitudes. A fire-report ISSUE number is the engineering half of most roadmap items — roadmap “link the store” is ISSUE-76. C and D are the acquisition side and barely touch A/B. E is a clock: the legacy .NET database goes offline in 3–6 months and the tarot migration is running right now.
Four documents read like roadmaps — they are one plan at four altitudes
| Document | What it actually is |
|---|---|
| q4-growth-plan-2026-09.md | The committed bet. One question, one mechanism, one scoreboard. |
| competitive-position §7 | The full sequence (Horizon 0/1/2). H1.1–1.6 is the Q4 bet, decomposed. |
| FEATURE-LIST.md | The ranked forward list. Collates both repos, flags the numbers that changed. |
| STRATEGY-brief §5 | The revenue-unblocking subset — effectively Horizon 0, stated separately. |
Read in that order and nothing conflicts. Use FEATURE-LIST as the working list and competitive-position §7 as the sequence.
Pricing, measured — this supersedes “reprice 3–4× or retire”
The last price rise already ran the experiment. Lifetime went $29.99 → $49.99 in Oct 2025. Same month year-over-year, which controls for the Nov–May slump that hit every SKU:
| Jun–Aug | units | realized price | revenue |
|---|---|---|---|
| 2025 | 637 | $27.24 | $17,355 |
| 2026 | 598 | $48.55 | $29,062 |
| change | −6% | +78% | +68% |
Keep vs retire is settled by arithmetic, not elasticity. Retiring only pays if enough lifetime buyers switch to annual. Break-even switch rate = lifetime net ÷ annual net over its life (annual $39.99 × renewal depth × 0.798 payout):
| lifetime price | annual ×1.5 | ×2.04 | ×3.0 | ×4.0 |
|---|---|---|---|---|
| $49.99 | 83% | 61% | 42% | 31% |
| $59.99 | never | 74% | 50% | 38% |
| $79.99 | never | 98% | 67% | 50% |
| $99.99 | never | never | 83% | 63% |
The only direct evidence: the +78% rise moved 13% of lifetime-inclined buyers elsewhere — a lower bound, since a price rise is a smaller shock than removal. Raising the price is what settles the keep/retire question — at $79.99, retiring can never pay. Lifetime money also arrives today; annual money arrives over 2+ years, conditional on a renewal depth nobody has measured per SKU.
At $39.99 annual is dominated on both sides: quarterly is $19.99/quarter = $79.96/year, and lifetime is $49.99. Annual is both the cheapest single year and cheaper than two years of quarterly. Quarterly outsells the new annual 480 : 126 — and quarterly is displayOrder: 1, shown first. Raise annual and reorder before adding any new SKU. Do not add a fourth subscription — the ladder is incoherent and a new rung makes it worse.
Execution: price fields only, three consoles, two traps
Verified: products_config.json carries no prices; there are no hardcoded prices in any .dart/.ts/.tsx; and the 30% discount works by swapping to a separate product id, not a coupon. So changing two price fields is sufficient.
| # | Console | full_purchase2025 | discount_full_purchase2025 |
|---|---|---|---|
| 1 | Google Play | $49.99 → $79.99 | → $55.99 |
| 2 | App Store Connect | $49.99 → $79.99 | → $55.99 |
| 3 | Stripe (web — easily missed) | $49.99 → $79.99 | $34.95 → $55.99 |
You make a new Price and repoint the link. /payments/get lists every active link with no dedupe by shortcode, so leaving the old one active shows two lifetime products at two prices. Deactivate it in the same change.
_payments.js's usdPriceByType() medians price across all history per SKU. After this, full_purchase2025 holds both $49.99 and $79.99 and its median drifts for months — every per-SKU revenue figure stays plausible and becomes wrong. Patch it to bucket by (SKU, month) before changing prices.
Every website CTA now routes through applyDiscount?code=WEBPROMO1, which serves the discount SKU — so web-acquired buyers pay $55.99, not $79.99. That is +12%, not +60%. Today that path is tiny (21 of 1,551 purchases), but the CTA fix was built to grow it. $55.99 is the price most new web users will actually see — choose it deliberately.
Every conversion baseline measured before 2026-09-07 was measured against a funnel whose exit did not exist — all 12 upgrade gates dead-ended on an unregistered /premium route. Do not scope pricing against those numbers.
The engine correction — relationship transits are not free
Both FEATURE-LIST §8b.1 and competitive-position 1.1 claim the engine already supports this — “it treats the composite as a natal chart” and “its orb system has a transit slot for synastry/composite.” Both supporting points are misreadings. Verified in the engine source:
CalcChart()has exactly two chart slots —dPosandTransitPos. A chart is natal (syn=0), synastry (syn=1), or transit (syn=2).applyComposite()consumes both slots to build the composite — it midpoints person 1 and person 2 intodPos. Line 869 only labels the result like a natal chart.- Transits to a composite need three charts at once — person 1, person 2, and the sky now. The two-slot model cannot express that in one call.
Orbs[]is[0]=natal, [1]=synastry/composite, [2]=transit— three orb settings for three chart types, not a transit slot for composite.
Three options, not equivalent. (1) Transits to the composite — what the plan implies; needs a third slot or a two-pass API. Real engine work. (2) Davison chart — the midpoint in time and space, so it has a genuine date/time/location and runs through the existing engine as an ordinary natal chart with syn=2 transits, no engine change. Not implemented anywhere. (3) Each partner's own transits side by side — works today with two existing calls, but answers a different question.
Name which one 1.1 is before estimating it. The asymmetry itself is real: the self matrix has six lenses, the relationship matrix has none.
The Q4 plan says instrumentation ships alongside, explicitly — because shipping the loop without it reproduces the exact failure the whole strategy diagnosed: real work shipped with no way to know if it moved anything.
The one thing blocking everything
Per-user event measurement does not exist — ISSUE-77, ISSUE-142. Metrics go to Prometheus, an aggregate time-series store. A uid label across 460,694 accounts is a cardinality explosion, not a one-liner. This was mis-priced as trivial and it is design work — a uid-keyed event collection that does not exist yet.
What it blocks: grading the Q4 bet · every gating, capping and pricing question · per-session funnels · experiment assignment.
app_version and platform now tag every metric at the single choke point every metric passes through. So “did release X improve anything?” is now answerable. “Did this session go A → B?” is not, and cannot be without an event store. That is the honest boundary.
Live state — verified against git, not against a prior summary
The fire docs' deploy-state claims lag reality in both directions.
The fire doc ranks this “TOP PRIORITY, committed but not pushed.” It is in origin/master (e90aa02). Pushed still is not deployed on the Node box — but the doc's framing is stale and should not drive the priority order.
12b9965 is in origin/master, not pending. FEATURE-LIST §C says to decide the language gate “before this is pushed” — that window has closed or is closing.
In the legacy astromatrix repo, local master is 8 ahead / 2 behind, and four of those commits are reverts. The ISSUE-106 GDPR tarot-deletion fix, the ISSUE-107 fix and the ISSUE-82 Sabian fix were each committed and then reverted.
Verified: RemoveAllData in Website/API/UserController.cs does not touch TarotReadingJournals today. The docs describe these as “fixed, committed, not pushed.” They are not in the working tree and not pushed. Whether the reverts were deliberate needs an answer before anything is re-applied — and the legacy database sunset makes it a dated question.
Uncommitted at time of writing
| Repo | Files | What it is |
|---|---|---|
| astromatrix-public | app/home/terms/ Footer.tsx style.css | ISSUE-148 (labelled 144 here — see B4) — the Terms URL printed in every store description 404s. Page restored, Footer link added. Blocks an ASO submission (Apple 3.1.2). The restored text has no auto-renewal clause — a lawyer's call, not an audit's. |
| astromatrix-flutter new-ui | products_config.json gated_content_widget.dart | ISSUE-147 / Horizon 0.1 — the false paywall bullets, removed. |
| astromatrix-nodejs | doc edits + 4 unpushed commits | ISSUE-143/144 fixes and a navigation-architecture UX audit. |
- Branch note: the shipping Flutter branch is
new-ui, 116 commits ahead ofmaster. Don't reason about Flutter state frommaster. - Website deploys work again. Push to
astromatrix-publicmaster→ Gitea Actions → live. Runnpm run buildlocally first and checkgit log origin/master..HEAD, because the server takes whatever is onorigin/master. The “no auto-deploy” note in older docs is stale. - Concurrent sessions are editing these docs.
docs/README.mdchanged on disk mid-session. Re-read before editing anything underastromatrix-nodejs/docs/.
The doc map
| Doc | Owns | Trust |
|---|---|---|
| FINDINGS-2026-09-11.md | Verified fleet numbers and the retraction list | read first |
| product/FEATURE-LIST.md | The forward list — what to build next | working list |
| product/README.md | Index for the product-design lane | current |
| docs/README.md | Index for the support / fire / migration lane | current |
| q4-growth-plan-2026-09.md | The committed quarterly bet | current |
| competitive-position-2026-09.md | Market position, Horizon 0/1/2, the AI-era answer, Memory as the fourth pillar | newest strategy |
| strategy-reference-2026-09.md | Ranked strategy map, visibility map, turnaround research, what NOT to do | levels predate 09-07 |
| built-but-disconnected-2026-09.md | Four assets built and unshipped: sky events, broadcast push, Big Three rarity, astrocartography | code-verified |
| acquisition-channels-2026-09.md | Creator seeding, PR, store editorial, the paid-UA math | no first-party data |
| STRATEGY-brief-2026-09-07.md | Product-consequence view of the active fires | current |
| SUPPORT-fire-manager-report | Master bug tracker — all ~147 numbered ISSUEs | authoritative |
| SUPPORT-fire-FLUTTER / NODE / PROCESS | Derived per-area dev specs | derived; FLUTTER self-flags stale |
| SUPPORT-ux-experience-issues.md | Non-technical UX complaints, counted by headcount | current |
| product/five-user-modes.md | Mode briefing pack, code-verified | levels superseded |
| product/app-map-and-changes.md | Verified app map — check here before claiming a screen is unreachable | current |
| user-knowledge-audit-2026-09.md | What the product knows about a user; the proposed knowledge model | new, untracked in git |
| INFRA-resilience-roadmap.md | DR / failover phases, open security items | current |
| MIGRATION-*.md | Legacy .NET → Node port | 3–6 month clock |
| SESSION-HANDOFF-2026-09-11.md | Ads, revenue economics, the website funnel | read before the test plan |
| paid-ads-test-plan-2026-09-09.md | Ads working doc | 4 generations of economics, 3 wrong |
Nine standing traps
Things that keep getting re-derived wrongly. Each one has cost a session already.
Don't propose a feature without checking whether it already exists. Event-triggered push, UGC/community, rewarded ads, the Guide deep-link and the in-chart South Node were each proposed as new work and each already shipped. Two of two “Bet”-tier items in one doc already existed.
Usage of an undiscoverable state is not a measure of demand.
/chartupcomingat 0.2% and Progressed at 1.3% are evidence of no route, not no demand.Never convert a destination into a state without leaving a signpost for one release. This has produced four confirmed “you deleted it” reports for features that still ship. ~25 of ~49 UI complaints are “I can't find things”; one named complexity.
“Too complex” and “too sparse” are both right. Six users want more on screen; one articulate user wants less. Solve it with a density mode — never by picking a side.
Never quote a truncated list as complete. Print a count, reconcile it, state N of N. Paginated Meta reads return partial data silently under rate limiting.
Verify writes by re-reading state — not by an exit code or a chained
echo.profiles= accounts (531,848).users= charts/people (1.24M). Not interchangeable, and they have been mixed up in published figures.Pausing a Meta campaign does not pause its ads. Audit
status, nevereffective_status.Committed ≠ pushed ≠ deployed ≠ released. For Flutter, add ≠ in a build a real user has.
The twelve-month concentration — four systems, not twenty features
Matrix
The first-level answer everywhere. Raw astrology becomes evidence, not interface.
Relationships
Persistent people, not synastry inputs.
Timeline
The only one of the four with no roadmap entry anywhere.
Memory
~4.5M user-written questions we already hold. The pillar that cannot be bought.
AI sits across these — it is not one of them.
A fifth thing not to postpone: surfacing. The product is already large. The next win is making users realise what they already have.
So: what now?
Horizon 0.4 — link the store, give reports a route. Small, verified, and everything downstream is unmeasurable without it.
Decide the lifetime SKU (0.2) and the language gate (0.5). Both are decisions rather than builds, and both are load-bearing.
Scope the event store properly. It gates grading the quarter, and it is not the one-liner it was priced as.
Ship Horizon 1.1 — relationship transits. It is the prerequisite half of the committed bet that the Q4 plan assumed already existed.
Leave the ads campaign alone until day 3 gives an honest CPI. judge it against $1.78, not $0.85 — see B9
Resolve the legacy .NET reverts before the database sunset makes it urgent.
AstroMatrix Orientation Map
The shortest route in. Nothing here is new information — it is the existing docs, arranged. The confusion has one cause: three separate tracking systems run in parallel, each with its own source of truth, and the roadmap lives in a fourth place that is not called a roadmap.
The three lanes
Every doc in the project belongs to exactly one of these. They are deliberately separate — a technical defect, a thing to build, and a customer who did not get an answer are different jobs with different owners. Mixing them is what makes the folder look chaotic.
Lane 1 · Fires
Technical defects
“What's broken, and has it actually been fixed?”
- Source of truth:
docs/SUPPORT-fire-manager-report-2026-08-26.md— 147 numbered issues, index table at the top, detail sections below. - Derived — never hand-edit:
SUPPORT-fire-NODE.md,-FLUTTER.md,-PROCESS.md. Regenerated from the master. - How to work it:
SUPPORT-fire-manager-PROCEDURE.md
Lane 2 · Product
What to build next
“What should we do, in what order, and why that order?”
- The forward list:
docs/product/FEATURE-LIST.md— ranked items plus every direct user request, weighted by repeat count. - The actual roadmap:
docs/product/competitive-position-2026-09.md§7 — the 3-horizon plan. This is the roadmap; it just isn't named like one. - Index:
docs/product/README.md
Lane 3 · Support
The inbox
“Did we drop a customer, and what are they telling us?”
- Queue & volume:
docs/SUPPORT-status-report.md— updated in place, never a new file per session. - Non-technical complaints:
docs/SUPPORT-ux-experience-issues.md— navigation, moved features, tone. Deliberately separate from bugs. - How to work it:
SUPPORT-manager-procedure.md
Two more workstreams that are none of the above
| Workstream | What it is | Where |
|---|---|---|
| Legacy migration | Porting the .NET API onto Node. A real, active workstream that was missing from the docs index entirely until 09-11. | docs/MIGRATION-*.md — 6 docs |
| Infra & disaster recovery | Windows/Ubuntu failover phases, incident history, open security items. | docs/INFRA-resilience-roadmap.md |
| The numbers baseline | Cross-cutting, and the one to read before quoting any figure. Part 1 is a list of numbers that were previously wrong and are still repeated elsewhere. | docs/FINDINGS-2026-09-11.md |
Which repo holds what
The product lane spans two repositories, which is the second-biggest source of “I can't find it”. Strategy docs live in the Flutter repo; feature and competitive docs live in the Node repo.
| Repo | Holds | State at time of writing |
|---|---|---|
| astromatrix-nodejs | Backend. All fire, support, product, migration and infra docs. | 4 commits unpushed |
| astromatrix-flutter | The app. The strategy layer: q4-growth-plan, strategy-reference, built-but-disconnected, acquisition-channels, product-strategy, user-knowledge-audit | 3 files uncommitted — two are Horizon 0 work |
| astromatrix-public | The website. Store/legal pages. | 5 files uncommitted, incl. the Terms fix |
| astromatrix-engine | Go calculation engine. | Clean |
| astromatrix | Legacy .NET. Only edit when Node genuinely can't do the fix. | Being migrated away from |
State of the board
see B5 These counts are a snapshot; the highest number known across the whole set is ISSUE-152.
Work that exists but isn't live
This is the gap most worth closing, because several items are already written. Four commits sit unpushed in astromatrix-nodejs — including the ISSUE-143 tarot recovery (31,507 rows, 8,866 users unblocked) and the ISSUE-144 account-deletion data purge, both compliance-relevant. The Flutter tree holds uncommitted edits to products_config.json and gated_content_widget.dart — those are Horizon 0.1 and 0.3. The website tree holds the Terms of Use fix (ISSUE-148), which gates the pending store-metadata submission.
Pushing auto-deploys and restarts production here, so these are decisions, not chores.
Fix the revenue shape and ship additive value before subtracting anything. There is no case for scoping a new limit until 0.1–0.4 have shipped and been measured — until then we don't know what this product converts at when its entrance is findable and its paywall describes the actual product. The store rating already moved ~3.78 → ~3.44 on a change that added something.
Five traps — things the docs still get wrong
These are the reasons two documents can disagree and both look authoritative.
Everything in
docs/analytics/is pre-correctionThose files still quote conversion inflated ~2.8× and volumes understated 14–44×, plus a retracted “tarot converts worst” framing.
five-user-modes.mdwas corrected; the analytics files were not.FINDINGS-2026-09-11.mdPart 1 lists every number to un-believe.Sample levels are not fleet levels
The usage-stats sample covers 16.6% of accounts and skews engaged. Use it to compare segments; never quote a level from it.
node scripts/_analytics.js verifyre-derives the headline numbers from production in ~2.5 minutes.The reach percentages are due a re-pull
Reports 7.8%, store 0.2%, harmonizer 2.8% — all quoted throughout the FEATURE-LIST, and all exactly the kind of level that moved. The ISSUE-77 instrumentation fix shipped on both platforms 2026-09-09, so a clean post-09-09 measurement is now possible and has not been taken.
Per-user measurement is mispriced as trivial
It is named as the blocker for most of the monetisation work, and the original “one-liner” estimate was wrong.
sendCounterwrites to Prometheus — aggregate-only. Auidlabel across 460,694 accounts is a cardinality explosion. This is an event-store design task.Usage of a hidden feature is not a measure of demand
Reports have no route; the harmonizer has one link; astrocartography's route isn't registered; 8,352 journal prompts were built and never surfaced. Low numbers on any of these mean nothing until they're reachable.
Where to start — the reading order
FINDINGS-2026-09-11.mdTwenty minutes. The verified baseline and the list of retracted numbers. Everything else assumes it.
competitive-position-2026-09.md§7The roadmap itself — the three horizons, with the reasoning attached to each item.
FEATURE-LIST.mdThe ranked list and, in the second half, every direct user request with its repeat count. This is where a new ask gets filed.
The fire report's index table
Just the table at the top — 147 rows with a status each. Skip the detail sections unless a specific issue matters.
docs/README.mdanddocs/product/README.mdThe two folder indexes. Both were stale and were brought current on 09-11 — every file on disk now appears in one of them.
Two different issues had both been filed as ISSUE-144 — the account-deletion data remnants, and the 404ing Terms of Use URL. The second was renumbered to ISSUE-148 across the master and derived docs, since ISSUE-144 is anchored in routes/user.js and in commit 1823e6e. The two folder indexes were also missing 12 files between them — FINDINGS, both role-procedure docs, the entire 6-doc MIGRATION family, and the three product docs written that day. All now listed.
Not covered here: this is a map, not a review. It takes each doc's own claims at face value except where a claim was already contradicted by another doc in the set.
The AstroMatrix Playbook
What the product actually is, what it has that nobody else does, what it's missing, how it makes money, and the four places new users could come from.
The sentence that isn't anywhere in the product
Practitioner-grade astrology in a consumer app — the charts the serious tools have, the packaging the popular ones have, for one payment instead of every year.
appears in no store screen · no upgrade dialog · no store listing · no web page
The four major subscription competitors average ~$230/year — Nebula $415, Sanctuary $240, CHANI $144, The Pattern $120. The $49.99 lifetime is 22% of that in year one and 7% over three; the $39.99 annual is 17%. “More than the competition in several areas, at under a fifth of the price” is literally true on both SKUs, and it is said nowhere. This is the largest unexploited marketing asset in the business, and it is free to use.
Name the capability gap, never the competitor. “Includes synastry and composite charts” is defensible and matches what competitors publish about themselves. “Better than CHANI” is not.
Never claim “no AI” or “written by humans” — position on specificity instead: your chart, not your sign, which is true, checkable, and attacks both leading competitors at once.
Use the price shape, not the price — “one payment, not every year” ages well; printed competitor prices don't.
Buying is a day-zero impulse
Median days from first open to purchase: 0. 63% buy same-day, 82% within seven days, and 57% buy before they have even registered. Time-to-value is therefore a revenue mechanic, not a satisfaction one — and anything sold on accumulated value (a content library, a subscription coach, a relationship history) is fighting observed behaviour until the first session is fixed.
The two opportunities that fall out of the charts-held table (see §C) are different jobs. The near one — 11,500 accounts holding 8+ charts who never paid: they already exhibit the strongest purchase predictor in the product and did not convert, so this is a conversion job — packaging, a reason, a findable way to pay. The far one — 300,710 accounts holding exactly one chart: all the headroom is here, but the question isn't “how do we sell to them”, it's what makes a second chart worth adding.
What we have that they don't, and what they have that we don't
Verified against the competitors' own published material. Composite charts appear in the practitioner tools and in none of Co–Star, The Pattern, CHANI, Sanctuary or Nebula.
The moat
Ours alone
- Real synastry and composite. No mass-market app has a composite chart. CHANI has no compatibility feature at all.
- Chart-specific, not sign-generic. CHANI ships one reading per rising sign, shared by thousands. Ours is computed from the actual chart.
- 29 bodies — Chiron, Eros, Psyche, Vertex, Part of Fortune, Eris, all four nodes. The consumer set stops at ten planets.
- Seven languages of interpretation content, ~59,700 documents. Every major competitor is English-first.
- Tarot integrated with the chart. Nobody in the astrology set has it — and it's our best-retained feature: 17 visits per account.
- Relocation, solar and lunar returns, progressed, draconic, astrocartography. Practitioner-tier, absent from consumer apps.
- One payment, not every year. Every major competitor is subscription-only.
- Relationship transits — not built. this doc says the engine supports it; it does not — see B1
The gap
Theirs, not ours
- A one-line verdict on a pair. The Pattern says “Soulmate” or “Complex”. We return four sliders — richer, and nobody can repeat it. The single word is what gets screenshotted.
- A friend/partner graph with daily updates. A second person is the reason to open the app. We have the charts, no graph, no daily pair state.
- A public-figures database. Free viral surface — try it on a celebrity. Our fields exist with zero rows.
- Audio — meditations, sleep stories. Can't be screenshotted, justifies recurring payment. Our full audio stack is shipped and serves only the Harmonizer. Needs an astrologer hire, not engineering.
- A guided beginner course. The Pattern has Astrology 101. Our Guide is a reference, not a path — 1.3% reach.
- A live human astrologer. The biggest revenue model in the category. We have none — but we have the practitioners.
- Journal prompts tied to transits. Turns a blank box into a reason to write. Ours is an unprompted shelf.
- Non-romantic relationship types. Nine of our twelve facets already work for a colleague or a parent. Packaging only.
The complexity complaint is not a complexity problem
Of ~49 interface complaints, one named complexity. ~25 said some version of “I can't find things.” The app is legible at the top of every screen and expert one level down — which is a much smaller problem than “the app is too complex”, and it means the fix is almost always the step between, not the screen. Three places that step is missing:
- The home tiles. Moon House, Astro House and Planetary Hour are three of the seven tiles on the first screen a new user sees, and they're the first thing that says “this isn't for you.” Translate them to consequence, or demote them.
- Between the slider and the aspects. The summary says Communication: +4; one tap down is “Mercury square Saturn”. Nothing bridges them.
- The chart-type names. “Draconic”, “Progressed”, “Composite” are offered with no one-line explanation of what question each one answers.
The loop, and the scoping rules that are easy to get wrong
Someone adds a partner → the app generates a live relationship reading, not a static score → they send it → the partner opens it with no install required → the partner sees content about themselves and the relationship they're in, not someone else's content → they create an account to unlock their own side → the pair is now live, and both have an ongoing reason to return.
You + Alex — strongest connection right now · current tension · what's changing · next important relationship window. Alex can see their side →
Not “here is your compatibility score.” And note: three of those four lines are relationship timing, which does not exist today. The static half — synastry, composite, the Matrix, the wheel, sharing — is all built. The timing half is the actual work.
- Aim it at people with 2+ charts who've never opened the relationship screen, and at Self-Explorers. Not Newcomers — half of solo accounts are Newcomers already holding 5.2 charts, for whom “add someone” is a bigger, riskier ask than “open a reading”.
- Do not gate the first reading behind the partner installing. The first user gets full value alone; the partner joining unlocks genuinely new shared state.
- Fix the cold-arrival landing before sending more traffic at it. Inbound relationship demand exists today and is being wasted.
- Cap the pair maths. 120 pairs at 16 charts, 79,800 at 400. Any “your notable relationships” feature needs pre-filtering.
The test — and it is a hard one on purpose. Not “people loved it.” Not “relationship engagement rose.” Not “shares increased.” Did attributable acquisition from this loop exceed enough of the existing user-loss rate to change the net growth curve? If it fails after a properly instrumented quarter, that's a real answer too. The only bad outcome is running it too loosely to tell the difference.
Premium is five annoyance-removals and one real capability
| Free | Premium | Shape |
|---|---|---|
| Ads, interstitial and rewarded | No ads | Annoyance removed |
| Reading gated every 10 → 7 → 5 views, tightening with use | Never gated | Annoyance removed |
| 10 saved charts | Unlimited | Limit removed |
| 10 saved reports | Unlimited | Limit removed |
| 5 people; basic city search | Unlimited people, exact-location search | Limit removed |
| No downloadable reports | PDF reports | A real capability |
The structural problem: five of six are “the annoyance stops”, and the reading gate tightens the more someone uses the app. So the heaviest, most engaged users — exactly the ones most likely to pay — experience premium mainly as nag-removal. That is why a retained paying subscriber, asked directly, could not name a single benefit beyond “no ads.”
If new limits are added anyway — ranked by upside ÷ backlash
Lower risk — gate a capability
Adds something rather than taking it away
- A daily cap on AI report generation. Best-shaped of the set: a genuine capability with real marginal cost, and a daily cap reads as fair rather than punitive.
- Export and share beyond PDF — chart images, shareable links, printable wheels. Same shape as the PDF gate, which users already accept.
- New relationship depth, gated as it ships. Pairs with the quarter's bet: gating new depth adds value without removing any.
Higher risk — takes away what people have
Every one lands on the cohort most likely to write a detailed review
- Tightening saved-chart or saved-report limits. Hits the practitioner segment directly — the ~11,500 unconverted high-chart accounts — and is the single most review-bomb-prone change available, because it breaks existing saved work.
- Lowering the 5-person limit. Worse: people store family and clients there.
- Transit date-range limits. Cheap, but invisible until someone hits it, which makes it feel arbitrary.
- Tightening the reading gate further. Already the most-complained-about mechanic, and it punishes engagement. Probably don't.
Any new limit must not retroactively remove something an existing user already has. The exposure is not hypothetical — the internal rating moved 3.78 → 3.44 on a UI rollout that added something.
Grandfather on observed usage, not purchase date — someone free who has already saved 40 charts keeps 40; limits apply to new accumulation. Say it out loud in-app — a visible “you keep everything you already have” notice converts the most likely 1★ reviewer into a neutral one, and silent enforcement generates review bombs more than the limit itself. And ship the additive capabilities first, so the pitch is “premium now does more” rather than “free now does less.”
Your work is great and closer to original astrology compared to The Pattern… but it is built for busy people. It gives them enough context to understand what is happening and then helps them apply that understanding to their lives.— a user explaining why she keeps leaving for a shallower competitor
The competitor is not beating us on depth or accuracy — the things premium could gate — but on packaging and time-to-value, which is free to fix.
Four channels, and only one where the audience already exists
1. The website → app handoff the standout
~89,000 monthly web users and 54,872 search clicks per 28 days already exist. There is zero conversion tracking between the site and the app, so nobody knows how much of that earned attention ever becomes an install — and a known parameter-drop bug breaks part of the handoff before it can even be measured. Every other item below is a bet on manufacturing new discovery in a saturated market. This one is finding out how much already-earned discovery is silently leaking. It's plumbing, not a growth hack, and it's the only place the audience is already there.
2. Creator seeding pilot sized, not greenlit
Not paid placements — free premium access to creators in exchange for organic content. The one anecdotal signal that has ever worked for this product is that people are blown away when shown the app in person, and competitors can't replicate that on camera because they don't compute this depth.
| The pilot | Sized |
|---|---|
| Budget | $750 — not buying posts, but covering attribution setup, assets, and a small reserve to boost the one or two that land. |
| Creators | 10 nano/micro, each posting one video of a real synastry reading generated live for two real people. Not a generic astrology-lifestyle script. |
| The bar | ≥391 attributable Android installs — i.e. did seeding beat what the same $750 would have bought through paid ads. That's 30–40 per creator. Stated as a threshold, not a prediction: no astrology-specific conversion data exists anywhere, which is exactly the gap the pilot fills. |
| Hard prerequisite | The referral attribution from the relationship loop has to be live first. Without it, seeded installs land in generic “organic” and the pilot produces an unmeasurable number instead of the figure it exists to generate. |
3. Paid advertising not now — a triggered re-test, not a permanent no
At $0.20 per user per month, twelve-month value is $2.40 — which sits right at the bottom of the iOS lifestyle cost-per-install range ($2–4) and roughly matches Android's average ($1.92). That's break-even-to-marginal, not wildly unprofitable. The honest caution is that paid-acquired users typically retain worse than organic. Raising revenue per user to $0.35 would put twelve-month value at $4.20 — comfortably inside the range, and a real case rather than a break-even one. So: re-run this math once the premium layer ships, and if it's ever tried, pilot Android-only, which is both cheaper and the platform with the confirmed decline. compare against $1.78, not $2.40, when judging a live campaign — see B9
Standard install ads send every clicker to the same generic first-open wizard regardless of what the ad promised. Deep-linked campaigns — creative built around one specific feature, dropping the clicker straight into that feature on first open — have a documented 1.2–1.6× conversion lift. And the plumbing already exists: the affiliate system already carries an install token through the Play Store referrer and matches it on first open. Extending that token to carry a feature target is reuse, not a new system. The obvious first creative: relationships, deep-linked straight into add-person — already the best-converting step in the app.
4. Press and store editorial free to attempt, unproven
- Apple's Featuring Nominations is a real, structured channel in App Store Connect. Lead time is 6–8 weeks realistically, up to three months ideally. Pitch the relationship loop, not “Chart Wrapped” — the loop is actually shipping this quarter. Working backwards from a mid-December ship, the submission window is early-to-mid November, and the feature must be demoable by then, not just described. Visual craft is reportedly the highest-weighted informal criterion, so an honest look at the relationship screen belongs in October.
- Google Play editorial is not an addressable channel at this scale — no public submission route, and editorial contact runs through an assigned account manager reserved for far larger publishers. The only real lever on that side is the algorithmic quality signal.
- “Chart Wrapped” is the stronger press story — genuinely hard to copy, because we retain multi-year chart history most astrology apps don't track. But it isn't scheduled, and pitching a feature with no build date is pitching vaporware. Park it until it has one.
- Fleet-data hooks — “the most common placement among X”, seasonal trend pieces — run on a real anonymised dataset most competitors don't have. Close to free to attempt, no guaranteed pickup.
Eighteen identified advocates, and nothing in the product serves them
Praise clusters on content depth and visuals, not on the interface — which confirms “the substance is better, the packaging isn't” from the opposite direction. Mostly from payers.
I have left a review and I recommend your app to everyone in my astrology groups. I'm a big supporter of you guys.— 44 saved charts, and still on the free tier
I do a lot of readings and teach astrology… I'll definitely be putting in another review, all positive.— lifetime buyer, promotes the app and the tarot deck unprompted
Yeah I can leave a review, I'll do it now before I forget.— the intent is perishable, and nothing captures it
I was using AstroMatrix as the storage for all of my clients.— one of five users running a paid practice on top of the app
People volunteer gifting, promoting and reviewing, and no feature supports any of those actions. There is no gift flow, no referral path, no share-a-reading built for advocacy, and no prompt that catches someone at the moment they say it. One user gifted a lifetime subscription to her sister in 2019 — organic gifting happens today with zero product support and zero tracking. Another left a review before buying, then bought lifetime because of the experience: the advocacy-to-conversion loop running entirely by accident.
The same sweep surfaces it in users' own words — my 5 star rating is going to be lowered if these issues and my requests for help are not answered
, after three unanswered messages. The lever that produces 5-star reviews and the lever that produces 1-star reviews are the same lever. An unanswered advocate becomes a reviewer with a grievance — directly relevant to the grandfathering exposure above: the existing advocate base is both the biggest buffer against a review-bombing and, if neglected, its source.
Four things to build, none needing new astrology: capture the moment (when someone expresses advocacy, that is the point to hand them the link — cheapest version is a prepared reply with a store link and a gift link) · a gift flow (it already happens manually, so it neither scales nor is measurable) · an individual referral path distinct from the creator-scale affiliate programme · a practitioner surface — client charts, sharing a reading with a client, export. The five professionals identified have the highest switching cost, the loudest professional networks, and the most to lose.
One user asked for Part of Fortune, got it, and came back with “thank you for hearing my requests” — a complainer converted into an advocate purely by visibly shipping what they asked for.
The value isn't only in the feature; it's in the reporter learning it shipped.
Already proposed, checked, and ruled out
Each of these was suggested in a previous pass and retracted after checking. They keep coming back, so they're written down.
- Don't gate the existing free chart and report depth. It's the one thing that produces the “blown away” reaction when the app is shown in person — locking it away kills the only organic signal that currently works.
- Don't merge the Report and Chart screens. Deliberately separate, both convert well, working as intended.
- Don't treat store-listing copy as a growth lever. Fix it once, cheaply — the current copy leads with “free and easy” instead of depth, and drops “horoscope” as a keyword — then stop iterating.
- Don't build offline calculation as a growth play. Real and worthwhile for practitioners handling other people's sensitive birth data, but it has no sharing mechanic and won't move installs.
- Don't replace the tarot free-text box. The 28-character typed question is the product — the only place in the app where users tell us in their own words what they want to know, and there are 4.5 million of them.
- Don't push tarot on the basis that it has “lowest reach.” That guidance was backwards: within any collection size, tarot users convert better.
- Don't chase organic social growth. A documented prior dead end for this brand — a 30k stale audience in a saturated niche. Targeted creator partnerships are a different thing and remain untested.
- Don't build non-English store screenshots. All non-English users combined are ~14%, split across 29 languages.
Decisions nobody else can make
The lifetime SKU — reprice, retire, or leave it
45% of purchases, one SKU at $49.99 is 41% of all sales, and it's the largest structural cap on lifetime value. The only change with zero grandfathering risk, and everything else in the pricing conversation is downstream of it.
Are we adding new limits at all, or only new capabilities?
The recommendation is additive-first, and there's a real case that a product already seen as harder to use than a cheaper competitor cannot afford a subtractive change. But this is a pricing call, not an analysis one.
Is localisation a premium feature or a free one?
Effectively decided free by default. Gating it afterwards becomes a withdrawal rather than an addition. See B3.
Greenlight the $750 creator pilot?
Yes/no, contingent on the relationship-loop attribution shipping first. The number it produces — a real, AstroMatrix-specific cost per install — doesn't exist anywhere today and can't be borrowed from industry benchmarks.
Commit to the November Apple nomination?
Free, and rides on work shipping anyway, but it forces a date: the relationship feature must be demoable by early November, and the visual pass has to happen in October.
Is “Chart Operator” a named product surface or not?
A workspace mode — client notes, last-seen, session history, pinning, transit alerts — for the practitioner segment. It doesn't fit inside the four-pillar story, so it needs an explicit yes or no rather than drifting.
The Matrix Question
The Matrix is the most-reached screen in the app after Home, and the second best at bringing people back. It is also the same screen every single time you open it. This is about closing that gap.
The Matrix is our simplification, not our complexity
It takes a birth chart — 29 bodies, several hundred aspects, a vocabulary that takes years to learn — and turns it into five words anyone can read: Purpose, Harmony, Love, Abundance, Evolution. That is the whole product thesis in one screen. Someone who knows nothing about astrology can open it and understand what they are looking at. No competitor ships the depth underneath it, and almost none ship a way in that is this gentle.
That last figure is the one to sit with. Months were spent treating “the app is too complex” as a Matrix problem. Of roughly forty-nine people who complained about the new interface, exactly one said the Matrix itself did not make sense. Everyone else said a version of “I cannot find a thing I already know exists.” Those are opposite problems with opposite fixes. Simplifying a screen that is already the simplification would make the product worse.
And because the Matrix's parts also render inside Home, the Moon screen and the weekly report, anything improved here lands on six surfaces at once. It is the highest-leverage screen in the product.
Three journeys — where people actually fall out
-
Journey one — the newcomer, thirty seconds after signing up
They finish a four-step setup — name, what they care about, birth date, birth place — and are dropped straight onto the Matrix, on the category they chose. This screen is the payoff for signing up. What they get is a radar chart and a grid of scored categories. After four screens of questions, the reward for answering is one more thing to navigate.
The break: we asked them what they cared about and then handed them a menu instead of an answer. Reading one interpretation is the sharpest activation threshold measured — activity jumps from 2.7% to 17.9% once someone reads one. We route them to the door and do not walk them through it.
-
Journey two — the returner, on their fourth visit
They tap the Matrix tab. They see what they saw last time. Same categories, same scores, same cards, in the same order. By default the screen shows everything it knows at once — their birth chart and every current transit, merged into one list. Nothing says what is permanent and what is passing. Nothing says what is happening today.
The break: a screen that never changes gives no reason to come back tomorrow. Retention is a smooth slide, not a cliff — 94% reach a second visit, 78% a fifth, 41% a twentieth. That shape is what “no fresh reason to return” looks like.
-
Journey three — the person who was sent a link
A friend shares their compatibility. The link opens the Relationship Matrix. Roughly half of these people are gone inside ten seconds — and among people arriving cold from a share link, 93% leave in under ten seconds. It is the worst-performing surface in the app. What greets them: no time filter at all, so there is nothing about right now. And in the default view, no Report button — the screen's main action is simply not rendered until you change a setting you do not know exists.
The break: this is also the best monetisation signal. Adding a second chart moves conversion from 20.7% to 51.2%. The relationship surface is where that happens, and it is the one finished least. 72% of accounts have never opened it.
Almost every complaint is the same complaint
Can no longer see yearly things like time lord and profection year.— Google Play review
Time lord and profection year are on the Summary tab. They were never removed. This is the fourth separate confirmed case of a live feature being reported as deleted — the others were weekly and monthly transits, solar return interpretations, and saved charts.
Everything looks the same, no visual differentiation to find what I need.— in-app support
This is the actual problem, said plainly. Not too much, not too hard — too flat. When every card looks like every other card, the eye has nowhere to land, and a person who knows the feature exists still cannot find it.
The new matrix system doesn't make sense, difficult to read through.— in-app support — the one complexity complaint in forty-nine
Worth taking seriously as a minority signal, not as a mandate. “Difficult to read through” is closer to the flatness complaint above than to a request for fewer features.
We are not failing to give people enough. We are failing to tell them what they are looking at.
Five moves, in the order to make them
-
1 · Make the screen different every day
highest returnsmall buildno new dataRight now cards are ordered by category — houses, then aspects — which never changes. But every card already knows whether its influence is active right now and how close it is to peaking. We compute this for every item and then ignore it completely. Sort by what is live and cresting today. Badge the active ones. The screen then changes on its own, every day, because those values move on their own.
Why this one first: it is the only fix on this list that gives people a reason to return tomorrow, and it needs nothing we do not already have. It also sidesteps the argument about which default view is correct — the screen becomes time-aware without anyone choosing a default.
-
2 · Answer the question instead of showing the data
high returnfree — no AI costEvery category already carries a pair of words and a count — Passion against Distance, Success against Obstacles, eight supporting influences against two challenging ones. The screen holds all of it and displays only the category name. Put one line under each category: “Intimacy & Romance — Passion 8, Distance 2.” That is the difference between a chart and an answer, and it is the thing the setup flow implicitly promised.
It also fixes a quieter problem: the score bars scale to each person's own maximum, so no number on the screen means anything in absolute terms. Words give it a direction even when the bar does not.
Keep the AI where it is. The deep written report stays behind the button — that is the payoff for tapping in, and it is genuinely good: people who reach a report spend 1.4 minutes there and 90% do not bounce.
-
3 · Fix the relationship first-run
highest commercial upsideone engineering unknownThe worst-performing surface is also the strongest conversion signal. Three things are wrong on arrival: the main action is invisible (in the default view the Report button is not shown at all); there is no “right now” (the solo Matrix has weekly, monthly and all-transits views — the relationship side has none); and we pick their partner for them (if an account has exactly two people saved, opening the Relationship Matrix silently pairs them and remembers it, changing what every other relationship screen shows, without asking).
The unknown: the hidden Report button was disabled because that specific report was thought too large to save. The reasoning does not match how the app actually saves it, and the real limit looks to be roughly fifty times larger than the report. One measurement settles whether it can simply be turned back on.
-
4 · Stop features from feeling deleted
medium returnreduces support loadFour confirmed cases of people reporting removals that never happened. The Matrix makes this worse in a specific way: influences that have finished quietly disappear from the list. Correct behaviour, never explained. Someone who saw an aspect last week and cannot find it today concludes we took it away. Two cheap moves: say when a view is empty and why, instead of showing a blank panel; and give finished influences a visible “ended” state rather than vanishing them.
The wider version: our depth is invisible — synastry 1.5%, composite 0.3%, the store 0.2%. We are not short of things to sell. We are short of people finding them.
-
5 · Don't lose people's reports
low volumetiny buildhigh indignation per occurrenceWhen a report is generated but fails to save — a network blip — we show a warning with a “View Now” button so the person can still read what they waited for. That button goes nowhere. It has never worked. Rare, but it lands at the worst possible moment: right after we told someone their thing was ready, and then lost it.
Three things not to do
Don't rename the Home buttons
The standing recommendation is that people cannot find weekly and monthly transits because the buttons do not say “transits”. But the two people who reported it asked “where is the weekly and monthly forecasts” — and the buttons say exactly Weekly and Monthly. They used our words and still did not find it. The label is not the barrier; where it sits on the page probably is.
Don't change the default view on instinct
Tempting to switch the opening view from everything-at-once to transits. But there is no A/B framework, no way to tell one session from another, and no app-version tag on stats — so we would be swapping a default we cannot grade. Bet 1 makes the screen time-aware without needing this decision at all. Make it later, with evidence.
Don't simplify further
One person in forty-nine called the Matrix too complex. The rest could not find things. Removing depth would answer the minority and damage the majority — and the depth is the only thing competitors cannot copy quickly.
The blocker: we cannot currently tell whether any of this worked
Everything measured is screen-level: how many people reached a screen, how long they stayed, whether they came back. Nothing tells us what they were trying to do, or what they tried just before giving up. Three things are missing, and each blocks grading the bets above:
- Session-level events — we count screen views, not app opens, so “visits” means something different from what everyone assumes when they read it.
- An app-version tag on stats — without it, no change we ship can be separated from any other change we ship. shipped 09-11 — see II §05
- Any A/B capability at all.
None of this should stop bets 1, 2 and 5 — they are small, obviously directionally right, and cheap to reverse. It should stop expensive or irreversible calls, like the default view, until the result can be read.
We can already tell whether a person has ever changed the Matrix's view filter, because choosing one leaves a trace and never choosing one does not. Nobody has ever counted it. If that number turns out to be 3%, the whole default-view debate is settled by the data rather than by argument.
Evidence: production scan of 11 Sept 2026 covering 102,600 tracked accounts — the first pull after the instrumentation fix released on both platforms, so the first trustworthy one. Complaint analysis from 45 days of in-app support (756 users) plus store and Facebook backlogs. Engineering detail behind bets 3 and 5 is filed as issues 131, 132, 136 and 137.
↑ Back to index topWhat AstroMatrix Knows
A code-level stock-take of everything the product currently stores, observes or could derive about a person — and how much of a user knowledge profile is already sitting in production, unread. The suspicion behind this audit was right, and stronger than expected: three of the five layers already exist as production data.
| Layer | State | What exists |
|---|---|---|
| Explicit facts | Working | Birth data, Big Three, 60+ chart and display preferences, language, selected partner — all persisted and synced. One critical fact is collected and discarded. |
| Behavioural signals | Built, never read | users.stats accumulates per-screen visit counts and dwell averages for every user, server-side, surviving reinstall. No product surface has ever read it. |
| Saved & intentional acts | Working | The Journal is already a timestamped, per-account, free-text-annotated store of everything a user deliberately kept. Rich. Under-modelled. |
| Derived patterns | 1 of 14 domains | Tarot stats prove the aggregation mechanism works end to end — and cover the one domain where the dimension was pre-classified at write time. |
| AI memory | Not wired | A complete conversational-memory schema, CrucibleChat, exists in Node against the same Mongo. Zero Dart files reference it. |
Five headline findings
The most valuable fact we collect is thrown away in the same frame it is collected. Onboarding step 2 asks “What do you need answers about?”, requires an answer to advance, and never persists it — not to disk, not to the server, not even as an analytics label. It has been that way since the step shipped in
df13e1ab(2025-12-22);git log -S selectedFacetshows nothing has touched it since.A per-user behavioural store already exists in production and is write-only.
stats.pagecountsandstats.avgsaccumulate on every screen change and sync to Mongo. The only read anywhere in the app increments a separate ads counter.Behavioural granularity is destroyed at three specific choke points — all fixable, none architectural. We can answer “how many people opened a relationship interpretation this week”; we cannot answer “has this user been reading relationship content”.
The AI knows nothing that distinguishes a returning user from a first-timer with the same chart. The richest AI request in the product sends a chart, a list of transit items, and a first name.
The interest taxonomy we would need to build already exists — as the 14 Self-Matrix and 12 Relationship-Matrix categories, already rolled up to the five onboarding facets by a tested mapping.
The ledger — identity and astrology, well covered
Every row was verified in source. Persistent means it survives app restart; AI means it reaches an AI prompt today.
| Knowledge | Stored where | Persistent | AI | Status |
|---|---|---|---|---|
| Name, birth date / time / town, lat / lng, timezone lib/models/user_profile.dart:8–47 → models/User.js:12–41 | users | yes | yes | Fact |
| Big Three — sign, degree, minutes, house lib/services/user_service.dart:1665–1734 | users.data | yes | no | Fact |
| Every saved person (“additional charts”) one users row per person, peer of the account's own | users | yes | when selected | Fact |
| Currently selected partner — the one durable user↔person link that exists lib/services/horoscope_service.dart:401–410 | prefs + local | yes | synastry | Fact |
| House system, zodiac system, 3 orb settings, smart orbs | users.settings | yes | no | Fact |
| 14 asteroid / point toggles, aspect & display prefs lib/constants/preference_keys.dart:55–95, 269–288 | users.settings | yes | no | Literacy signal, unused |
| Language | users.settings | yes | partly | Half-wired |
| Declared area of interest — Love / Evolution / Abundance / … lib/views/user_wizard_screen.dart:365–470; never reaches saveUser() | nowhere | no | no | Discarded |
Every chart, aspect, transit, progression, synastry, composite, draconic and return is computed on demand and cached disposably. No computed-chart collection exists anywhere — the engine README still lists “Database integration” and “Caching layer” as Not Implemented.
Behaviour — a real store exists, and nothing reads it
| Signal | Written by | Stored where | Server | Status |
|---|---|---|---|---|
| Per-screen visit counts | ScreenTimerService | users.stats.pagecounts | yes | Write-only |
| Per-screen average dwell seconds | running mean | users.stats.avgs | yes | Write-only |
| Ads seen | AdsService | users.stats.ads_seen | yes | Write-only |
| Chart-type dwell, per type | chart_screen.dart | folds into pagecounts | yes | Write-only |
| Tarot sessions + per-deck dwell | TarotReadingSession | pagecounts + Prometheus | yes | Write-only |
| Gating session count / view count | ContentGatingService | local only | no | Device-bound |
| Which interpretation, aspect, planet or house was opened | — | — | — | Not recorded |
| Which person a report was about | — | name inside a title string | — | Not recorded |
| Searches, AI questions, per-user notification opens, sessions | — | — | — | Not recorded |
Where the granularity goes
Three choke points, each a deliberate past decision that was right for its own purpose and wrong for this one. The most consequential is a single line.
Choke 1 · The route already carries the answer, and drops it
_sanitize() splits on ? and keeps the head — screen_timer_service.dart:165–166, mirrored in metrics_service.dart:326
The component name and its category are present in the route, at the call site, and are discarded one line later. We record that a user opened 200 interpretations; we never record which. This is the cheapest high-value fix in the document.
Choke 2 · Prometheus cannot carry a person
All metrics go to Prometheus counters and gauges. A session id is generated and deliberately left unattached — the comment at metrics_service.dart:30–52 spells out why: an unbounded label is one time series per session, a cardinality bomb. It ends: “It exists so session-scoped events can carry it once a real event store exists to receive them.” No such store was ever built.
Choke 3 · The one content metric carries category only
component_opened_total is labelled {category, source} — never the component name, never a user. Aggregate by construction. Firebase Analytics is not a second source: its whole surface is purchases, shares and four main-user diagnostics, it is disabled on web, and it joins to no user id we hold.
What reaches the AI today
Three AI endpoints exist. Two are reachable from the app, and the third is only used for translation.
Path 1 · Chart reports — POST /ai/calculatereport
Path 2 · Matrix reports — POST /ai/generateDataReport the main AI surface
Path 3 · GPTService — generic completion, one caller: translation
- No conversation exists. No chat surface, no follow-up, no history.
specificQuestionis plumbed end-to-end in Node and unreachable from the client — the “ask about this reading” capability is built and unwired. - AI history is a file, not a record.
logAIInteractionappends toai_log_YYYY_MM.jsonby rewriting the whole file, stores a first name and no user id, races across a clustered process, and has auser_feedbackfield that is always null. It is prompt QA, not memory. - A working memory schema is one repo away.
CrucibleChatdefines per-user history with per-turn metadata, running stats, and acurrentStateblock documented as “re-injected into next prompt”. It belongs to the quiz funnel, not the app — but it is a reviewed design for exactly this layer, already running against the same Mongo.
ISSUE-39/122 threaded language through five AI paths. /ai/generateDataReport is not one of them, on either side. Every Matrix category report — the main AI surface — is still English-only for every user regardless of their language setting. Narrower than ISSUE-122, and not covered by it.
The model: facts, signals, inference
Twelve relationship lookups supportrelationship_interest: high. They never supporthas_relationship_problems: true.
Five layers, separated so an observation can never be promoted into a fact by accident. Facts and saved acts mostly exist already; signals need scoring; inference needs receipts and decay.
Action weights — intentional acts outrank passive ones
| Action | Weight | Why |
|---|---|---|
| Journal comment written | 5.0 | Effort, in the user's own words |
| Tarot question asked | 5.0 | An explicit statement of need — already captured today |
| Report generated | 4.0 | Deliberate, costly, often gated |
| Interpretation saved (heart) | 3.0 | Intentional keep — already a journal write |
| Entry reopened later | 3.0 | Sustained, not momentary |
| Person selected for a reading | 2.0 | Directed attention |
| Interpretation opened | 1.0 | Passive |
| Screen dwell > 20s | 0.5 | Corroboration only — a backgrounded app looks identical |
| Screen opened | 0.2 | Navigation noise |
| Interpretation un-saved | −3.0 | Negative signal; prevents stale interests |
Scored with a 30-day half-life for current interest and 180-day for long-term themes, damped by novelty — ten opens of one component means attachment to that item, not interest in its domain. Nothing is reportable below 3 events across ≥2 distinct days.
An inference is only ever written with its receipts
The whole knowledge document is derived and disposable: delete it and it rebuilds from journals, events and settings. That single property is what makes “reset my personalization” a one-line delete, and what makes changing the scoring rules safe to ship.
The taxonomy already ships. The interest dimensions exist twice over, as the 14 Self-Matrix categories — Path & Career, Intimacy & Romance, Security & Roots, Soul Path & Karma, Emotional & Mental Health and ten more — and the 12 Relationship-Matrix categories. They are already rolled up to the five onboarding facets by a tested mapping in component_image_helper.dart:247–273, so Intimacy & Romance → Love is the same vocabulary the discarded onboarding answer speaks.
Retrieval, not ingestion
The profile is the index, not the payload. Each request assembles a budgeted context from ranked candidates: relevance × recency × actionWeight.
| Budget | Tokens | Contents |
|---|---|---|
| Always sent | ~600 | Chart placements and current transits for the subject · the screen the user is on · language, knowledge tier, length and terminology preference |
| Retrieved, capped | ~1,200 | People named or implied by the query (≤2) · journal entries matching the category or person (≤5) · prior AI reports on the same category or person, as stored 2-line summaries (≤2) · saved interpretations in the category, titles only (≤5) · derived patterns at confidence ≥0.6 and AI-eligible (≤3) |
| Never sent | any budget | Raw event logs · full journal text · unrelated categories · low-confidence inferences · anything the user has not consented to, or that touches a sensitive category |
Summarising at write time rather than read time keeps this flat: a prior report costs ~40 retrieval tokens instead of ~1,500. A fully personalised Matrix report grows from roughly 2,500 to 4,000 prompt tokens — about +60% on a gpt-4.1-mini call, a rounding error against the revenue those reports gate.
Trust, and one thing to fix regardless
The design goal is that a user reading their own profile page thinks “yes, that's fair” — not “how do you know that?” Every stored claim carries its provenance, and inferred ones are dismissible in one tap, with the dismissal actually suppressing the inference rather than hiding it.
Forbidden outright: never infer or store sexual orientation, gender identity, religion, health or mental-health status, pregnancy, substance use, immigration status, financial distress, or the state of a relationship. These are exactly what astrology browsing tempts. Reading twelve “Intimacy & Romance” interpretations is a content preference. The permitted claim is always “you have been reading X”, never “you are Y”.
[Details of an open security item are withheld from this public copy.] Harden journal access before enriching what journals store.
What to do first
Ordered by value ÷ effort, and specific to what already exists. Everything here is capture or repair — none of it depends on the model being right, and all of it makes the model possible.
Persist the onboarding facet
One field, one line, today. A self-declared life priority collected from 100% of new users at the moment of highest intent, currently discarded between two function calls. Before any AI work it can order the home screen and pick the first Matrix tab.
Read
stats.pagecountsAlready in Mongo for every user. One read-only query answers “which categories does this person actually spend time in” — no new capture, no migration, no client release.
Stop stripping the query string
One line in each of two files converts the most-used behavioural counter in the app from “opened the component screen 200 times” into real content history.
Write
subjectPersonIdsonto journal entriesAdditive to an existing
Mixedfield, so no migration. Unblocks the entire relationship model — and fixes the real bug where reopening a saved relationship report renders whoever the partner is now.Harden journal access
Do it before anything above enriches journal data.
Fix the Matrix AI language gap
Small, self-contained, and the last English-only AI surface in the product.
Read the tarot questions
Already in Mongo, already indexed, and the only unmediated user language the product holds. Even a manual read of a few hundred would inform the taxonomy and the prompt more than any inference engine.
Then start scoring
Phase 2 and beyond only after the capture gaps are closed.
Item 1 alone is arguably worth more than Phases 2–5 combined per unit of effort. It should not wait for a roadmap.
Full audit with every file:line citation, the complete data structures, the event taxonomy, the privacy controls and the five-phase roadmap: docs/user-knowledge-audit-2026-09.md. Companion docs: built-but-disconnected-2026-09.md · strategy-reference-2026-09.md · product-strategy-2026-09.md.
AstroMatrix Quick Wins
Everything here is plausibly under half a day. Anything bigger was deliberately left out — the last block says what, and why. This is the most recently written of the seven, so where its issue numbers differ from the others, its are the current ones.
✓ verified in code or live on 2026-09-11 § from the docs, not re-verified
A · 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.
B · Ship what is already fixed
Zero new code. ✓ Verified against git on 2026-09-11.
| # | Repo | Action |
|---|---|---|
| B0 | all — 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.
C · 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. see B1 |
| 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. |
Source: astromatrix-nodejs/docs/DEV-quick-wins.md. Companion to docs/ROADMAP.md, which carries the reasoning behind the price change and the engine correction.
§D · End matter
How this was assembled
Each of the seven briefings was read in full from its published source and restated here under one type system, with its structure and its claims preserved. Nothing was summarised away, and no claim was strengthened: where a source hedged, the hedge is kept.
The one thing added is §B. Those ten conflicts were found by reading the seven against each other — none of them is noted in any individual document, because each document was written without sight of the others. Where a conflict was resolved, the rule applied was that a claim checked against source code or a live console beats a claim carried forward from another document. Where the sources simply drifted rather than disagreed — issue counts, unpushed commit counts — that is said plainly rather than resolved to a false single answer.
§C merges the verified/retracted number tables that three of the documents each carried a partial copy of. Cross-references marked see Bn inside the briefings point at the place a claim in that briefing is contested.
This is a snapshot. Every deploy-state claim, commit count and issue number in it was true on 10–11 September 2026 and will drift; the underlying repository documents stay authoritative. The standing rules in the sources apply to this page too — re-derive any fleet level with node scripts/_analytics.js verify, and check git rather than a document before acting on any “committed but not pushed” claim, including the ones printed above.
Seven sources, all published privately to claude.ai: AstroMatrix Ground Truth · AstroMatrix Orientation · AstroMatrix Orientation Map · The AstroMatrix Playbook · The Matrix Question · What AstroMatrix Knows · AstroMatrix Quick Wins. Each dossier header links back to its original.