Timeline & Journey Status
Oct 3, 2026 · @Mark
Bet 4 and FEATURE-26 are both committed and pushed to new-ui. Neither is shipped: the agreed state is pushed → awaiting device verification → shipped only after a list and grid pass.
Decisions settled
FEATURE-30 is decided: the organizing story is the journey, not a taxonomy — AstroMatrix takes you from who you are, to how you connect, to what's happening now, to where those patterns are going. Store-listing test it has to compress into: Your chart. Your relationships. What's happening now. What comes next.
- AI is not a fourth pillar. It is an interaction layer across Matrix / Relationships / Timeline. Making it a pillar would organize the app around implementation technology rather than the user's life.
- Maturity changes presentation, not the IA. Beginner: sequence explicit. Developing: steps open into richer surfaces. Mature: scaffolding falls away. No permanent separate beginner app.
- The wizard stays as it is. Step-level abandon data does not exist yet, so cutting steps now would be intuition dressed as evidence.
- No cross-type Next/Back chaining.
component_screen.dart's_goNext/_goBackonly ever sequences one homogeneous list per screen — "reuse the Next button" would have been new infrastructure disguised as reuse. - The facet payoff is preserved. The wizard keeps routing to
/matrix?tab=<facet>; newcomers are not redirected to Home, which would break the thread I said what I want answers about → show me that answer. - Timeline V1 ranking is temporal, not astrological-strength. It ranks on position relative to begins/exact/ends. It makes no claim that one aspect is intrinsically stronger than another.
The list/grid defect
The first bet 4 implementation would have been invisible to every user in list view, while reading as shipped. It was caught only because the reuse question was asked directly.
What was wrong. The duration label was written into AstroItemCard._buildGridLayout. But AstroItemCard.build() returns AstroListTile whenever isGrid == false (astro_item_card.dart:128), and MatrixCategoryPanel picks grid vs list from a user-toggleable view mode (matrix_category_panel.dart:120-138; _MatrixItemsList passes isGrid: false at :529 and :565). Anyone whose panel was in list mode would have seen nothing.
Why it matters beyond this change. This is the verify-the-effect-not-the-deployment trap in UI form: it compiled, the analyzer was clean, the code existed, and the label was absent from half the audience. A code-path citation showing a call site exists is not evidence that the call site renders.
How it was fixed. The logic moved into the one shared component both branches already used:
AstroHelper.temporalStatusLabelholds the phrasing, once.AstroDateLabelholds the rendering, once, behind a new opt-inshowTemporalStatusflag that defaults tofalse— so no other caller's display changes.- The status substitutes for the prefix+date in the same single Row, so the row keeps its height. This matters: grid tiles are fixed at
mainAxisExtent: 158, and a second line would have broken them. - Wired at exactly two call sites. Zero duplicated rendering, both layouts covered.
A latent force-unwrap was fixed on the way: AstroDateLabel's listUsesChartColors branch re-derived _formatFullDate(displayDate!) instead of using the computed value, and would have thrown whenever displayDate was null — already reachable through the isList range path.
Code paths touched
All in astromatrix-flutter, all uncommitted and unpushed. flutter analyze is clean on every file — the one remaining warning in astro_list_tile.dart:186 is pre-existing, confirmed by diff.
| File | Change | Shared? |
|---|---|---|
lib/utils/astro_helper.dart |
New temporalStatusLabel — the three bet 4 phrasings from begins/ends/exact |
Yes — single source of the phrasing |
lib/widgets/astro_date_label.dart |
New opt-in showTemporalStatus; status substitutes for prefix+date in the same Row; listUsesChartColors branch now uses the computed value |
Yes — single source of the rendering, used by both layouts |
lib/widgets/astro_item_card.dart |
Passes showTemporalStatus: true on the grid path; the duplicated block was removed |
Call site only |
lib/widgets/astro_list_tile.dart |
Passes showTemporalStatus: true — the branch the Matrix/Home list panels actually render |
Call site only |
Not mine, left untouched: lib/services/purchase_service_stub.dart was already modified before this session.
The already-committed half of bet 4 is ISSUE-137(a) (48c3725d): the active-first, nearest-peak sort at matrix_service.dart:757-774. It is also committed-not-pushed, so both halves ride the same push decision.
FEATURE-26 — two surfaces, only one solved
Do not read "facet routing exists" as "identity-led Home is done." These are different surfaces and only one is covered.
- Post-wizard routing: solved. The wizard's facet step already asks what the user wants answers about, already stores it, and already routes to
/matrix?tab=<facet>(user_wizard_screen.dart:385-389). A brand-new user does land on facet-personalised identity content. Nothing to build. - Home prioritisation: not solved. FEATURE-26's own evidence sits on Home — 37.8% of first-visit activity there, only 2.7% reaching an interpretation — and its claim that Core Themes is ranked fourth is still true in code. Verified at
home_screen.dart:1114-1121: Daily Overview (1), Current Journey (2), Important Events (3), Core Themes (4).
Shipped 2026-10-03 (51e6be0d). Identity is now integrated into the daily panel rather than promoted as a competing one. Two importantBirth items lead; the daily take(8) is untouched, so no transit content is lost. They lead rather than interleave on purpose — mixed together, a natal trait and a passing transit become indistinguishable, which is the confusion the panel exists to resolve. Core Themes stops being its own panel, so Home carries three instead of four, and _buildCoreThemes / _getTitleForImportantBirth were deleted with it.
The panel is renamed Daily Overview → What Matters Now: it now holds enduring items too, and the old name implied everything in it was about today. The HelpService section key stays 'Daily Overview' so existing help content still resolves.
The framing correction that made this right. Home was already fully personal — every panel comes from the same chart through filterByType. So this was never about adding personalisation. It is about enduring vs. time-bound: importantBirth items are true of you always, daily items are true this week. That is what two natal items at the top actually buy.
Applied to everyone for V1, deliberately. Weighting it by maturity needs a demonstrated-acts signal that does not exist, and the row's own "first ~3 sessions" wording is exactly the session count we do not gate on. Measure first; differentiate when there is a real signal to differentiate on.
Open dependencies
Three rows were filed this session. None is implemented, and all three need a change in a repo that is lookup-only here.
| Row | What it unblocks | Blocked on |
|---|---|---|
| FEATURE-80 | Strength-weighted ranking (Timeline V2) | The external CalculateHoroscopes engine emitting a per-aspect orb, then mapping it into MatrixItem.orb — the field exists at matrix_models.dart:74 but nothing populates it. Owner is whoever owns the legacy engine; outside both repos. Deliberately off Timeline V1's path. |
| ISSUE-539 | Per-cohort onboarding drop-off analysis | A uid-joinable step event. Node must declare the receiving schema fields with or before the client change. |
| ISSUE-540 | Slicing retention fixes by version instead of by date | An appVersion field on the pagecounts write. Same schema precondition. |
The precondition that would eat both silently. Mongoose discards undeclared keys without erroring. Ship the client half alone and the client gets a 200, the fields never exist, and each row's own verifyRoute reads as zero — indistinguishable from "the build never shipped". Each row carries this warning explicitly.
Two gaps from the original audit remain unfiled: no in-app experiment/flag framework, and pre-auth being fully invisible. Both were lower priority in that audit.
Critical path
[embedded content: critical path · 4 steps, 1 parallel track]
Bet 4 is the only box with code in it today. Everything between it and Timeline V1 is a decision or a schema change, not Flutter work — and the strength-ranking track below never blocks V1.
Verification gap
Bet 4 is not verified and should not be described as shipped.
What was actually checked:
flutter analyzeclean on all four touched files, with the one remaining warning confirmed pre-existing by diff.- The render path traced by hand: both
isGridbranches now reachAstroDateLabelwithshowTemporalStatus: true.
What was not checked:
- The label's real appearance on a device, in either view mode.
- That the three phrasings fire at the right times against live transit data —
temporalStatusLabelreadsHoroscopeService.instance.transitDate, so its output depends on real item dates, not on anything the analyzer sees. - Whether the substituted status reads better or worse than the date it replaces, on narrow tiles where
AutoSizeTextwill shrink it.
What closes it: one device pass in both list and grid mode. That is a requirement, not a nicety — the defect above was precisely a case where one mode worked and the other silently did not. Analyzer plus code-path tracing justifies committing; it does not justify claiming the UI effect.
Needs your call
- Device verification. Both commits are pushed and neither is shipped. One pass in both list and grid mode closes it. This is the only thing standing between the current state and "done".
- Whether the Add Someone tile ships ungated. Its copy is settled and it belongs to the beginner and developing tiers — but those tiers have no gate yet, by decision. The tile is additive and demotes nothing, so it could ship to everyone now instead of waiting. Not assumed either way.
- Nothing else is blocked. The maturity gating is deliberately deferred until a demonstrated-acts signal exists, and the three tier presentations below are defined so that wiring them later is not a design exercise.
Add-someone tile — agreed direction
Kept deliberately lightweight, so it reads as a natural next move inside the current surface rather than the start of another onboarding framework:
- Existing Matrix visual language — model on
_buildReportItem(matrix.dart:2057). - One clear relationship-oriented line.
- One action.
- No bespoke illustration, no new card system.
This is the only genuinely new widget in the whole plan; everything else reuses what ships today.
Tier presentations — defined, not wired
Presentation progression over one IA, not separate IA. Defined now so that wiring them later is not a design exercise; the gate waits on a real demonstrated-acts signal.
| Tier | What they see |
|---|---|
| Beginner | Facet payoff + What Matters Now with identity context + Add Someone |
| Developing | The same, plus a Timeline peek / what's-next entry |
| Mature | Full Matrix / Relationships / Timeline, onboarding scaffolding largely gone |