Product Roadmap¶
Last updated: May 2026 (PR18 — StartHerePage render crash fix; PR19 — full report opened to free users; May 17–19 2026 — LifeSimulatorPage standalone route, Foundation 7 sub-sections + What Drives You quiz, 6-dot alignment, check-ikigai-alignment endpoint, per-goal weekly focus/reflection (PR51), daily insights i18n, Mind & Body 2-step, html2canvas-pro v2 PDF fix, display=swap font fix, Admin Research tab, register research consent)
Current Status: Version 1.0 — Full Platform Live¶
All three core stages plus payments, coaching ecosystem, and admin tooling are implemented and functional.
Stage 1 — Discover ✅¶
- 47-question psychometric assessment with adaptive ordering (expanded from 45; Introvert sub-scale reliability fix — May 2026)
- 13-phase personalised report (rule-based insight panels — no AI calls)
- PDF export, print support
- "How to Read" guide modal
- 6-month retake cooldown (enforced in
StartHerePage.tsx) - Post-report feedback widget (rating + note, saved to
reportFeedback/{uid})
Stage 2 — Design ✅¶
- Life Design — 5-step wizard (requires Discovery + Explorer plan)
- Guided Ikigai builder (4 quadrant cards, archetype suggestions, statement builder)
- Vision → Mission → SMART Goals chain-of-derivation flow
- Life Path Simulator (Acceleration / Mastery / Contribution)
- Strategic Audit (SWOT + habits + capability gap)
Stage 3 — Do ✅¶
- Growth Loop — habit tracking, weekly reflections, monthly reviews (requires Discovery + Life Design + Explorer plan)
- 30 / 60 / 90 day review cycles
- Per-habit progress rating and mastery tracking
- Trend indicator
Payments ✅¶
- Stripe Checkout via VPS Express (
/api/stripe/create-checkout-session) - Explorer plan: RM 99/year recurring subscription with
planExpiresAtgate - Coach plan: RM 599 (year 1) → RM 199/year (renewal) via Stripe Subscription Schedule ✅
- Annual renewal extension via
invoice.payment_succeededwebhook - Subscription cancellation handling via
customer.subscription.deletedwebhook (plan reverted,coachAppStatuscleared, coaches doc deactivated) - Promo codes: free-access, percent-off, fixed-off (admin-managed)
- Agent referral commission pipeline —
?ref=AGENTCODEcaptured insessionStorage→ passed in checkout metadata → posted to agents-service oncheckout.session.completedviaPOST http://127.0.0.1:5003/api/agents/internal/record-commission. Live-tested and confirmed: RM29.85 first sale at 15% Starter, repeat-customer bonus (+5%) working, idempotency on duplicatesessionIdconfirmed, wrong key returns 401 ✅ - Bug fixed (April 2026):
userDatawas used before declaration instripe.js— coach checkouts would crash withReferenceError. MoveduserDatalookup to run before theplan === 'coach'guard. planExpiresAtexpiry gate on all paid features- Admin complimentary access —
plan: 'explorer'with noplanExpiresAt= permanent free full access (admin-granted, no Stripe needed) hasPaidAccessfix — admin-granted explorer plans (noplanExpiresAt) now correctly returntrue
Coaching & Community ✅¶
- Certified coach directory + application workflow
- Full 5-step coach onboarding with payment gate: apply → admin pre-approve → Stripe payment →
payment_receivedstate → admin activation ✅ coachAppStatusfield on UserProfile ('approved_pending_payment' | 'payment_received') for clean gate state management- CoachPortal payment gate screens (payment form, payment confirmed, activating fallback)
- Annual renewal reminder banner in CoachPortal (amber ≤30 days, red if overdue)
- Coach portal with full client roster and archetype/goal visibility
- Coach referral system with unique per-coach referral links
handleGrantCoachAccessfixed — now also creates/reactivates coaches doc viaupsertCoachFromUser+ refreshes Active Coaches state ✅handleRevokeCoachAccessfixed — now also deactivates coaches doc viadeactivateCoach+ filters coaches state ✅- Team reports
- Workshops
- Read-only share link for mentors/parents
Admin ✅¶
- 10-tab AdminDashboard: Overview / Analytics / Engagement / Users / Profiles / Coaches / Workshops / Promo Codes / Feedback / Organisations
- Admin report viewer (full paid-tier, PDF-exportable)
- Admin cooldown reset + plan manager
- Promoting to coach:
upsertCoachFromUser+ coaches state refresh - Demoting from coach:
deactivateCoach+ coaches state filter - Admin complimentary access grant (permanent Explorer, no Stripe required)
- Feedback analytics tab (rating distribution + written notes)
- Coach applications — full lifecycle: Pre-Approve, Reject, Delete, Activate (only on
payment_received)
Organisation Management ✅ (data layer) / 🔲 (UI)¶
Organisationdata model —inviteCode,coachIds,plan,planExpiresAt,seatLimitactivateOrgPlan/deactivateOrgPlan— batch-updates all member plan fields- Org subscription covers all members (denormalized
plan: 'explorer'on each member user doc) - Members join via
/register?org=INVITE_CODE—orgId+orgRole: 'member'auto-set - Pending: Org Admin dashboard UI, invite link UI in AdminDashboard, auto-assign on join
Access Control & Docs (April 2026)¶
- Access gate refactor —
canAccessWellnessProfileandcanAccessFinancialPlannow self-contained (includehasPaidAccesscheck internally) Pricing.tsxfree plan copy corrected to accurately describe Phase 1 access onlydocs/access-control.mdcreated — single source of truth for all 3 access tiers, gate functions, org rules, and Explorer grant pathsdocs/architect-analysis-prompt.mdrewritten — includes mandatory doc-reading table, correct MongoDB/VPS references, current gate signatures,compatibilityEngine.ts,rateLimiter.js,/compareroute
Compatibility Engine (April 2026)¶
compatibilityEngine.tsextended with 4 new output sections:dosList,dontsList,approachGuide,traitsToWatchOut,conflictFlashpoints- 37-change plain-language rewrite of all compatibility output text
Rate Limiter (April 2026)¶
middleware/rateLimiter.jscreated — per-useruserLimiter(120 req/min keyed onreq.user.uid), admin bypass- Applied to
GET /meandPUT /meinroutes/users.js - Global IP limiter raised from 300 to 800 req/15 min
Self Mastery (April 2026) — menu name: Self Mastery, file: SelfMasteryPage.tsx¶
SelfMasteryPage.tsx(prev.SelfInquiryPage.tsx) — 5-phase structured self-coaching module (Data → Reactions → Patterns → Reality → Action)- Gated by
canAccessSelfInquiry(Discovery completed + Explorer/Coach plan) - AI-guided at every phase via
AiGuidecomponent; prompt builders inselfInquiryService.ts - Session persistence:
saveSelfInquirySession()writes toselfInquirySessions[]on user doc viaPUT /api/users/me - Past sessions injected into AI context for continuity across future sessions
/self-inquiryroute added toApp.tsx; nav entry added toLayout.tsx
People Blueprint (April 2026) — menu name: People Blueprint, file: PeopleBlueprintPage.tsx¶
PeopleBlueprintPage.tsx(prev.CorporatePage.tsx) — workplace relationship mapper with per-relationship compatibility guides- Gated by
canAccessCorporate(Discovery + Life Design completed + Explorer/Coach plan) - Users add manager / peer / subordinate relationships by archetype;
computeCorporateGuide()produces scores + playbooks computeTeamIntelligence()produces team-wide cohesion score and archetype spread- AI team coach via
AiGuidewithbuildTeamCoachSystemPrompt()context-aware to the full org chart saveCorporateData()writes tocorporateRelationships[]on user doc viaPUT /api/users/me/corporateroute added toApp.tsx; nav entry added toLayout.tsxcanAccessCorporateandcanAccessSelfInquirygate functions added tojourneyGates.ts- All documentation updated:
docs/access-control.md,docs/architecture.md,docs/features.md,docs/user-journey.md,docs/architect-analysis-prompt.md
File Naming Alignment (April 2026 — first pass)¶
All 7 page files renamed to match their menu display names exactly to eliminate confusion during development:
QuestionnairePage.tsx→DiscoveryPage.tsxLifeDesign.tsx→LifeDesignPage.tsxWellnessProfile.tsx→WellnessPage.tsxFinancialPlan.tsx→FinancialPage.tsxSelfInquiryPage.tsx→SelfMasteryPage.tsxCorporatePage.tsx→PeopleBlueprintPage.tsxProfileCompare.tsx→RelationshipLensPage.tsx
Imports in App.tsx, route JSX, and internal export default function names all updated in lockstep. Sub-folder step components (lifeDesign/, financialPlan/, wellnessProfile/, growthLoop/) are NOT affected.
File Naming Alignment (April 2026 — second pass, plain-language labels)¶
All 6 remaining page files renamed to match updated plain-language navigation labels:
DiscoveryPage.tsx→StartHerePage.tsxLifeDesignPage.tsx→DesignMyLifePage.tsxGrowthLoop.tsx→DailyGrowthPage.tsxWellnessPage.tsx→MindBodyPage.tsxFinancialPage.tsx→MoneyPlanPage.tsxReport.tsx→MyReportPage.tsx
Imports in App.tsx, route JSX, and internal export default function names all updated in lockstep. Sub-folder step components unchanged.
Plan Assembly Engine (April 2026)¶
Shipped across 6 PRs to turn Life Design from a blank-page exercise into a one-click drafting experience. Full flow + invariants documented in life-design.md §3.5.
- PR1 — Ikigai bulk draft.
POST /api/ai/draft-life-plan scope=ikigaidrafts all 4 quadrants from the user's Profile + Foundation + Life Wheel + Wellness + Money data. Empty-only fill; never overwrites. 10 AI calls / user / hour rate limit. - PR2 — Life Blueprint bulk draft. Same endpoint with
scope=alldrafts Vision + Mission + all 6 pillars (goal/anchor/timeBound/whyMatters). Server returnssoftStatesmap so pillars render friendly hints when inputs are thin (student-starter,no-money-plan,no-wellness). - PR3 — Completeness nudge polish. Modal in
DraftMyPlanButtonshows which source modules are filled vs empty with "Fill →" deep-links. - PR4 — Consistency review.
POST /api/ai/check-life-plan-consistencyruns after Save Progress and surfaces up to 3 real conflicts between inputs and plan in a dismissable indigo banner with plain-English fixes per conflict. Shares the 10/hr rate-limit bucket. - PR5 — Grounding + model fix.
- Fixed tune-with-AI outage.
gemini-1.5-flashhad been retired by Google ("models/gemini-1.5-flash is not found for API version v1beta"). All three AI endpoints now default togemini-2.5-flash. - Life Path Simulator data now reaches Gemini. Added
server/src/utils/careerPaths.json(mirror of the client data). Server resolvesarchetype × foundation.pathChoiceintopathIdentity,coreFocus,year1Milestone,year3Milestone,year5Milestone,balanceNeededand injects them into the prompt. NewLIFE PATH GROUNDINGrules force Vision to quote the 5-year milestone and the Career pillar to quote the year-1 milestone. - Zero-income handling.
hasMoneyPlannow requiresincome > 0 OR assets > 0(previously any Money Plan snapshot counted, which silently suppressed soft-states for students with no income). NewhasNoIncomecontext flag +no-incomesoftState flip the Finances pillar into income-creation mode: "Land first paying gig within 90 days" instead of a savings target. - PR6 — UX polish (make one click obvious). Both Ikigai and Life Blueprint steps now show a prominent purple/violet/rose gradient card with a "✨ One click" badge, a dynamic headline that counts empty fields ("Draft all 4 quadrants from what you've already told me" → "Auto-fill the N empty fields" → "X of N already filled — leaving those alone"), a large "Draft my Ikigai · 1 click" / "Draft my Life Blueprint · 1 click" button, and a green success toast after the draft ("Drafted N quadrants — edit anything that doesn't sound like you", auto-dismisses in 6–7 s). The card auto-hides once every target field is filled.
- PR7 — Life Wheel ↔ pillar alignment enforcement. Audit found that even though
lifeWheel.areaStatementswas in the payload, the prompt never commanded Gemini to reconcile each pillar's SMART goal with the matching Life Wheel 12-month statement — it only anchored Career on the Life Path Simulator year-1 milestone and otherwise left alignment to chance. Added a mandatory LIFE WHEEL GROUNDING block toplanSystemPrompt()that: (a) treatslifeWheel.areaStatements.<pillar>as the authoritative 12-month target for every pillar, (b) scales urgency by Life Wheel score (≤4 → ≤90-day timeBound + daily/weekly anchor; ≥8 → sustaining cadence), (c) tightens timeBound + anchor for pillars infocusZones(the important ∩ improve intersection), and (d) resolves Career conflicts between the Life Path Simulator milestone and the Life Wheel career statement in favour of the Life Wheel (more recent, more personal), surfacing the tension via acareer-vs-path-tensionsource tag that the consistency review picks up. No client change — the AI drafts themselves now align to the user's own pillar statements. - PR8 — "Why this score?" per-pillar reality statement. Scoring a pillar 1–10 without explaining why was too thin a signal — the AI saw
score=4+ a 12-month target and had to guess what was broken, leading to generic goals ("exercise 3x/week") when the real constraint was elsewhere ("broken sleep, phone in bed"). AddedlifeWheel.currentReality.<pillar>(6 new optional fields on the Life Wheel shape) surfaced as a second textarea under each pillar's slider in Step 3. The placeholder and prompt reword themselves by score band: low (≤4) asks "what specifically feels broken?", mid (5–7) asks "what's working, and what still isn't?", high (≥8) asks "what's the edge?". 20-char minimum (softer than the 30-char gate on area statements). ServerbuildPayloadpasses the fullcurrentRealitymap to Gemini; the LIFE WHEEL GROUNDING block now treatscurrentReality.<P>as the most authoritative signal about what each SMART goal should attack, with explicit worked examples in the prompt. Existing users (pre-PR8) have nocurrentRealitywritten — the prompt falls back to Foundation + score band and citeslifeWheel.currentReality.<P>:missingin sources, so their existing drafts still work. No migration; opt-in for returning users. - PR9 — Integration bundle: make currentReality + whyMatters first-class. Audit after PR7/PR8/PR8b surfaced that the new fields weren't fully flowing through the review + alignment loop. Six coordinated fixes: (1) Consistency review now sees
currentReality—buildConsistencyPromptUSER payload includes the fullcurrentRealitymap, and a new conflict-type "pillar goal ignores stated constraint" is explicit in the system prompt with examples; pillar dump also extended to includetimeBoundandwhyMattersso the reviewer can flag missing deadlines on focus-zone pillars. (2) Per-pillar alignment dots add a 6th "Reality fit" check —computeAlignmentChecksnow takescurrentRealityand produces a green/amber/red dot with hover hint that quotes the user's own words back at them ("Your goal sidesteps the constraint you named: 'broken sleep, phone in bed'"). Threshold 15/6 — softer than Life Wheel statement since currentReality is shorter prose. Empty state when user hasn't filled it. (3)whyMatterssources are now visible — drafted sources for "why this matters to me" were stored infieldSourcesstate but never rendered; SourceChip now renders below the textarea when sources exist. (4) 429 error messages distinguish endpoints — draft hit says "Draft limit reached (10 AI calls/hour, shared with consistency review)", consistency hit says "Consistency review limit reached (10 AI calls/hour, shared with plan drafts)" — so users know which budget they just spent. (5) Deleted legacyIdentityBaseline.scores— 1–5 per-area identity scores retired in April 2026 when Life Wheel's 1–10 scale replaced them; type field lingered with a "silently dropped" comment. Gone now. (6) Deleted deadstepTimestamps— declared for a "Foundation updated — refresh drafts?" banner that never shipped; field was written nowhere, read nowhere. Clean shape. No server migration (fields were all optional). - PR10 — "Make It Stick" replaces Strategic Audit. Step 5 was a 647-LOC orphan: Gap Map + SWOT grid + archetype copy + 90-day plan + threat/emotion picker + capability gap — overlapping with everything upstream (Life Wheel scores reappeared, Foundation pathChoice reappeared as archetype templates, pillars reappeared as threats). Rewrote as a 3-block confirmation flow that reuses everything already written: Block 1 surfaces the focus-zone pillars (Life Wheel
focusZones→ elseimportant ∩ improve→ else top-2 lowest scores) with each pillar's goal, anchor, timeBound, currentReality side-by-side for one-glance validation; manual override still possible. Block 2 pre-seeds one daily ritual per focus pillar using the user's own anchor text (single click, no AI) — rituals flow straight into the Growth Loop Habit Tracker. Block 3 is the capability gap with a new "Draft from my plan" AI button: new server scopePOST /api/ai/draft-life-plan scope=capability-gapreturns ONE learnable capability (not a trait like "discipline") that unlocks the most focus pillars, grounded incurrentReality+ focus-zone pillar goals, shares the 10/hr rate-limit bucket, JSON-mode withCAPABILITY_GAP_SCHEMA. Step renamed from "Strategic Audit" to "Make It Stick". Legacy fields (swot.*,strategicThreats[]) stay on the schema for back-compat but are no longer edited here. Dead code deleted: Gap Map, SWOT archetype copy-list, 90-day milestone cards, threat/emotion picker,buildAutoSuggestions,ThreatCard,computeGapType, path-drift banner (remains in Foundation where it belongs), Opportunities draft hint. Net −246 LOC on the step. - PR11–PR15 — AI hardening pass (April 2026). Post-PR10 audit surfaced five loose ends. Fixed as a single batch:
- PR11 Rate-limit every AI endpoint. Only
/draft-life-plan+/check-life-plan-consistencyhad a bucket./chat(AiGuide, used everywhere) and/generate(self-mastery commitment, foundation synthesis, corporate engine) were open — one abusive user could burn the Gemini budget. Added per-user hourly buckets:chat= 30/hr,gen= 20/hr,draftstays at 10/hr. All four endpoints now emitX-RateLimit-{Kind,Limit,Remaining,Reset}response headers so the UI can surface budget to the user later. Shared helperconsume(kind, uid)+applyRateHeaders(res, kind, info)— no duplicated bucket logic. - PR12 Delete dead
aiService.ts.generateLifeArchitecture(200 LOC) andgenerateAIInsightwere the last callers of the legacy/api/ai/generatescope for a feature deprecated in Oct 2025 — zero importers across the client. File deleted entirely; no remaining references. - PR13 Persist focus-pillar override + seed fallback. "Pick different focus pillars" in Make It Stick was a component-local
useState— reloading the page reset to Life Wheel focus zones every time. Added optionalfocusQuarterOverride?: string[]toLifeDesignData;StrategicAuditStepnow reads it on mount and writes on every toggle. "Pre-seed rituals from my pillar anchors" now falls back to the pillar's goal text when the anchor is empty, so users who skipped anchors can still seed rituals (previously the button silently did nothing). Legacyswot?/strategicThreats?fields marked explicitly optional onLifeDesignData— read path preserves them on save so historical data round-trips without pollution ofEMPTY_DATA. - PR14 Tighten consistency-review gate.
handleSavefired the consistency check as soon as any of Vision/Mission/a single pillar had content, which meant half-drafted plans generated noisy false-positive conflicts mid-flow. Gate now requires Vision filled + Mission filled + ≥4 pillar goals before calling/api/ai/check-life-plan-consistency. - PR15 Soft-state payload restructure. Server used to return
softStates.finances = "student-starter" | "no-money-plan" | ...as a bare key, andLifeBlueprintStephardcoded a four-key English map to render a friendly sentence. Schema changed to{ key, userText }tuples; the prompt now instructs Gemini to emit the prose verbatim so copy can evolve without a client redeploy. Client reader accepts both shapes for one release of back-compat (old plans still render). - AiGuide prompt cleanup. DesignMyLifePage's Life Design AiGuide still mentioned "SWOT Opportunities/Threats" in its FIELDS_STATUS and the
swotReadybranch of NEXT_FOCUS — stale references from pre-PR10. Replaced with capability-gap + daily-rituals status and astickReadybranch. Voice stays identical; only the field names change. - Rate-limit chip in the UI.
client.tsnow snapshotsX-RateLimit-{Kind,Limit,Remaining,Reset}from every/api/ai/*response into a per-kind store and dispatches anai-rate-limitwindow event. New<RateLimitChip kind="…" />component subscribes and renders only when remaining ≤ 3 (configurable threshold) — tone shifts amber at 1, rose at 0 with refill-in-X-min copy. Wired into Life Blueprint one-click draft, Strategic Audit capability-gap button (kind=draft), and AiGuide widget header (kind=chat). - PR16 — Legacy SWOT + threat-picker fields fully retired (April 2026). PR10 left
lifeDesign.swot.{opportunities,threats}andlifeDesign.strategicThreats[]on the schema as optional read-only fields because three places still surfaced them: CoachPortal's "Self-Identified Threats" panel, CycleReviewsTab's Day-90 "Threat Watchlist (from SWOT)" callout, and selfInquiryService's strategic-threats context block. Pre-rollout window made the call easy: retired all three reads, droppedswot?+strategicThreats?+StrategicThreatinterface +GapType+StrategicHabit.threatIdfromLifeDesignTypes, removed the legacyswot?shape fromUserProfile.lifeDesigninapi/db.ts, deleted the swot/strategicThreats preserve blocks inDesignMyLifePageload path. Migration scriptserver/scripts/backfill-drop-legacy-lifedesign.js$unsetslifeDesign.swot,lifeDesign.strategicThreats, andlifeDesign.identityBaseline.scores(PR9-retired) from every user doc; supports--dry-run. Run on prod cleaned 12/12 docs; verification dry-run shows 0 remaining.identityBaseline.coreStatementis intentionally kept — it's still the persisted home of the user's "I am…" statement (FoundationStep, IdentityFoundationStep, GuidedIkigaiStep all read/write it). - PR17 — Identity flattened to a single source + StrategicHabit rename + dead-step cleanup (April 2026). Pre-PR17, the user's "I am…" statement lived in two places that had to be kept in sync:
lifeDesign.identityBaseline.coreStatement(legacy) andlifeDesign.foundation.synthesizedIdentity(Phase-1 Foundation). FoundationStep was dual-writing on every keystroke. Worse, four<IdentityBanner>instances acrossLifeWheelStepand the standalone banners inLifeBlueprintStep/StrategicAuditStep/DailyGrowthPagewere silently reading a non-existent top-leveldata.synthesizedIdentityand renderingundefined— a bug introduced when the field was nested underfoundation. Fixes: dropped theIdentityBaselineinterface andlifeDesign.identityBaseline?field fromLifeDesignTypes.ts; removedidentityBaselinefromEMPTY_DATAand the load path inDesignMyLifePage(the existing legacy-foundation seeding block survives — it reads the raw mongo shape so users created pre-PR17 still inherit their statement once); removedonIdentityChangedual-write from FoundationStep and its parent invocation; removedidentityBaselineprop fromGuidedIkigaiStep; rewrote all 6IdentityBannerreads todata.foundation?.synthesizedIdentity; updated AiGuide FIELDS_STATUS + sidebar gate. RenamedStrategicHabit.threat→StrategicHabit.pillar(more accurate post-PR10 since it stores the pillar label, not a threat); 8 references updated acrossStrategicAuditStep.db.tsUserProfile.strategicHabits[]accepts both shapes (pillar?: string; threat?: string) for one release of back-compat;DailyGrowthPage+CycleReviewsTabmappers readsh.pillar ?? sh.threat ?? ''. Deleted deadlifeDesign/IdentityFoundationStep.tsx(zero importers — superseded byFoundationStep). Migrationserver/scripts/backfill-flatten-identity.jscopies any unmigratedidentityBaseline.coreStatementintofoundation.synthesizedIdentity(3 docs on prod), then$unsetslifeDesign.identityBaselinefrom every doc that still carried it (11 docs cleaned); idempotent — verification dry-run shows 0 remaining.
May 2026 (May 17–19)¶
- html2canvas-pro v2 PDF fix —
MyReportPage.handleExportPDFnow passesskipValidation: true+ clamps dimensions toMath.min(Math.max(dim, 1), 16383). PreventsValidator.validateDimensions()throw from v2.0.2 breaking change. - Google Fonts display=swap fix —
client/index.htmlchanged fromdisplay=optionaltodisplay=swap. AddedInter-fallback@font-face (size-adjust: 107%) inindex.css. Resolves missing word-spaces in report text. - Growth Loop i18n —
DailyGrowthPagedaily insights now useuseTranslatedData('dailyInsights')hook. Malay/Chinese/Tamil users see translated archetype insights on Today tab. - Per-goal weekly focus + reflection (PR51) —
GoalsKanbanTab: each goal card has weekly-focus block{ focus, why, weekKey }and 3-question reflection{ q1, q2, q3, alignmentScore, weekKey, savedAt }. Stored atprofile.growthLoop.goalKanban[id].weeklyFocus+.weeklyReflection. - LifeSimulatorPage standalone — Moved from Foundation Section 7 embed to
/life-simulatorroute. Added to sidebar 'Your Foundation' group. Foundation Section 7 now shows committed-path badge only. - Foundation 7 sub-sections + What Drives You quiz — Foundation redesigned with 7 accordion cards. New required 'What Drives You' motivational quiz (Stability/Variety/Recognition/Connection/Mastery/Impact) gates the rest. Stored at
user.motivationalDriver. - 6-dot alignment system per pillar — Added Reality fit + Identity alignment dots to the existing 4-dot system.
POST /api/ai/check-ikigai-alignment— New endpoint. Returns{ verdict: 'coherent'|'gaps'|'scattered', critique, suggestion? }.- Mind & Body 2-step — Wellness Goals step removed; goals now source from
user.wellnessProfile.goalsin Life Blueprint Health pillar. - Financial goals at
financialPlan.goals— Goals step removed from Money Plan; goals surface as read-only cards in Life Blueprint Finances pillar. - Admin Research tab (11th tab) — Added Research tab with consent tracking, exclusion flags, per-sub-scale Cronbach alpha, item-level stats.
- Register research consent checkbox —
Register.tsxopt-in checkbox stored asuser.researchConsent: boolean. - JourneyProgressStrip 7-stage — Updated from 5 to 7 stages. Stage 5 = Life Simulator.
AI Cost Optimisation — PR38–PR43 (April 2026)¶
Life Design AI was burning tokens on redundant data every turn. Fixed across three PRs.
- PR39 — 5-step → 4-step UI. Merged the old "Life Wheel" (Step 3) and "Life Blueprint" (Step 4) into a single Step 3 ("Life Wheel & Blueprint"), making "Make It Stick" Step 4. How-to-Use modal, AI Coach system prompt, and
nextFocussequencing updated. No data model change. - PR40 — Remove orphan consistency endpoint.
POST /api/ai/check-life-plan-consistencywas never called after PR14 tightened the gate — the trigger was removed but the endpoint survived. Deleted the route (53 lines),buildConsistencyPrompt,CONSISTENCY_SCHEMA,validateConsistency, and the clientcheckLifePlanConsistency()helper. −255 net lines. - PR41 — Dedicated
one-pillarscope.draftPillarGoalCard()inLifeBlueprintStepwas callingscope=all(generates Vision + Mission + all 6 pillar goals — ~800 output tokens) and discarding 95% of the output to use one pillar's goal text. Added a newscope=one-pillarthat generates a single{ goal, anchor, timeBound, whyMatters }object (~150 output tokens). New server utilonePillarSystemPrompt()+ONE_PILLAR_SCHEMAinlifePlanPrompt.js; new client helperdraftOnePillarGoal()inlifePlanService.ts. ~85% output token reduction per "Draft goal" click. - PR42 — Compact AiGuide system prompt. Every chat turn sent a 3.5 KB system prompt. Three cuts: (1)
buildFoundationContextBlock()(15+ verbatim Foundation fields, ~1 KB) replaced with a 5-line compact block (identity + path + 2 joy signals + top wellness/money goal, ~250 chars); (2)FIELDS_STATUSnow only emits incomplete/vague fields — a fully-progressed user sends zero redundant "filled" lines; (3)COACHING_VOICE_RULES+CROSS_MODULE_RULES(~1.3 KB verbose scaffolding) replaced with a 12-rule inline block covering the same coaching intent (~400 chars). Net ~1.5 KB saved per turn on a typical 15-turn session: ~22 KB. - PR43 —
FieldAIActionsdebounce + cache + soft cap. No safeguards on the 5-action tune dropdown meant rapid sequential clicks could fire parallel AI calls or drain the 10/hr budget on one field. Added: 3-second cooldown gate after each real API call; result cache keyed byfield:action(cache hit applies the previous result without an API call, shown as a brief "Cached ✓" badge); soft cap at 5 API calls per component mount with a confirmation dialog before continuing.
Messaging & Notifications (June 2026)¶
- Conversations stored with string UUID
_id(viarandomUUID()) instead of MongoDB ObjectId — fixes all server-sidefindOnelookups toDbId()helper added toconversations.jsfor backward compatibility with any existing ObjectId docsgetOrCreateConversation()now awaited before creating coaching notifications — eliminates dead/messageslinks- Notifications for coaching events embed
?conv=<uuid>for direct deep-link to the conversation thread Messages.tsxauto-selects the first conversation when no?conv=URL param is presentDELETE /api/notificationsendpoint added — clears all notifications for the authenticated user- "Clear all" button added to both notification panels in
Layout.tsx(sidebar + top bar dropdown)
Clean Frontend Deploy (June 2026)¶
- VPS was accumulating old Vite-hashed build assets (8,847 files);
rm -rf /opt/personality-app/public/assetsnow run before each SCP upload c:\My Apps\hostinger\deploy-personality.ps1created — builds, clears remote assets, then uploads fresh build in one commanddocs/deployment.mdupdated with manual steps and script reference
Layout & Sidebar UX (June 2026)¶
- Desktop top header hidden on authenticated inner pages (avoids double navigation chrome)
- Sidebar collapsible on inner pages (Report, Wellness, LifeDesign, Financial)
- Notification dropdown uses fixed positioning to prevent overflow/bleed on inner pages with collapsible sidebar
Firebase Removal & Codebase Cleanup (June 2026)¶
- Firebase SDK,
firebase-admin, and allVITE_FIREBASE_*env vars completely removed - Stale VPS files deleted:
firebase.js, orphanedagents.jsroute, one-off data scripts (checkorgs.js,checkuser.js,deletedup.jsetc.) - Stale local files deleted:
SelfInquiryPage.tsx,workshops-sharelinks-feedback.js,careerPaths.json.bak, emptyserver/src/models/ - Architecture doc corrected: removed phantom
/api/compareroute entry, fixedselfInquiry.sessionspath, removedfirebase.jsentry from server structure
Discovery Assessment Render Fix (May 2026)¶
Bug fixed: StartHerePage.tsx — fatal render crash introduced when questions 46–47 were added.
- Root cause: a spurious () invocation after .map() ();})()) caused React to throw TypeError: This expression is not callable on every render, showing only the ErrorBoundary "Something went wrong" screen.
- Fixed to })} — map result is correctly returned as Element[], not invoked.
- ErrorBoundary in App.tsx also converted from function to class component (function components cannot catch render errors — required getDerivedStateFromError / componentDidCatch).
- Service worker cache bumped to v10 to clear stale bundles on user devices.
- Stale comment references to "45-question pool" updated to 47 in scoring.ts and StartHerePage.tsx.
Instrument Refinement & Journey UX (May 2026)¶
Questionnaire expansion (45 → 47 items):
- Introvert sub-scale α was 0.309 (below acceptable threshold) — only 2 items (44, 45) producing unreliable internal consistency
- Added items 46 and 47 to the Introvert sub-scale:
- 46: "I prefer to think through my ideas privately before sharing them with others."
- 47: "I find that one-on-one conversations feel more natural and rewarding to me than group settings."
- SUBSCALE_ITEMS.Introvert in admin.js updated: { items: [44,45,46,47], reverse: [] }
- MIN_ANSWERED_FOR_STABILITY in scoring.ts updated from 45 → 47
- 15 archetype questionnaire items revised for clarity and precision (prior session)
- 5 compound/ambiguous wellness questionnaire items rewritten: Q4, Q5, Q9, Q10, Q20 in wellnessQuestions.json
Admin Research Stats fixes:
- wellnessCompleted data bug: admin stats were checking u.wellnessCompleted (a boolean never written) causing wellness engagement to always report 0%. Fixed to check u.wellnessProfile (the actual object field written by the app).
- Force distribution bug fixed: admin.js now reads r.force.force when r.force is an object (MongoDB stores force as { force: string, ... } not a bare string)
- Research Stats tab added to AdminDashboard with per-sub-scale Cronbach alpha, item-level statistics, archetype × force distribution, and module engagement breakdown
Journey flow UX fixes:
- JourneyProgressStrip.tsx stage order corrected — was: Discover → Wellness → Life Design → Do → Fund. Now: Discover → Wellness → Money Plan → Life Design → Growth Loop (matches actual prerequisite chain)
- MyReportPage.tsx — journey bridge replaced with 5-state gate-aware bridge using liveProfile (live-polling context):
- State 1: no wellnessProfile → Go to Mind & Body
- State 2: no financialPlan → Go to Money Plan
- State 3a: both done, no selectedPath → Start Life Design
- State 3b: selectedPath set → Continue Life Design
- State 4: lifeDesignCompleted → Start Growth Loop
- MyReportPage.tsx — guide modal stage order corrected: Discover → Wellness → Financial → Life Design → Growth Loop
- journeyGates.ts — getJourneyProgress() next-step chain updated to include Wellness and Money Plan steps between Discovery and Life Design
- Dashboard.tsx — Next Best Action now includes "Build Your Money Plan" between Wellness and Life Design
- RelationshipLensPage.tsx — GateScreen "Complete Life Design" hint updated to mention Mind & Body + Money Plan as prerequisites
Architectural Decisions¶
- VPS JWT Auth: Auth is handled entirely by the VPS (
/api/auth/*endpoints issuing 30-day JWTs). Firebase has been completely removed. All data is stored in MongoDB on the VPS. - React 19 + Vite 6: High-performance frontend with fast HMR.
- Tailwind CSS 4: Utility-first styling, consistent design system.
- Modular Scoring: Scoring logic (
scoring.ts) is fully decoupled from UI — safe to test independently and portable to future API or mobile integrations. - Data-Driven Content: All report and card content is driven from JSON data files — no hardcoded copy in components.
- 6-Month Assessment Cooldown: Prevents score drift from mood-based retaking. Uses existing
createdAttimestamp — no new DB fields required. - Rule-Based Insight Panels: The 4 insight panels (self / direction / decision / evolution) in the report are fully deterministic from
framework.json— no AI calls. Same archetype combination always produces the same output. - Stripe via VPS: Payment credentials never touch the client. Webhook signature verification on the VPS Express server prevents spoofed plan upgrades.
Known Architecture Items (Not Yet Addressed)¶
Three simultaneous✅ Fixed March 2026 —onSnapshotFirestore listeners (Layout + ProtectedRoute + DailyGrowthPage) — could be centralised to a React Context to reduce redundant reads.UserProfileProvidercreated insrc/contexts/UserProfileContext.tsx, mounted inmain.tsx. Now pollsGET /api/users/meevery 30 seconds. Layout, ProtectedRoute, and DailyGrowthPage all consumeuseUserProfile()instead of owning their own listeners.✅ Fixed March 2026 — Both files decomposed into focused child components.LifeDesignPage.tsxandGrowthLoop.tsxare 1000+ line monoliths — candidates for step-component decomposition.DesignMyLifePage.tsx(~500 lines) now delegates tosrc/pages/lifeDesign/(AssessmentStep, FuturePathStep, GuidedIkigaiStep, LifeBlueprintStep, StrategicAuditStep + shared LifeDesignTypes).DailyGrowthPage.tsx(~330 lines) delegates tosrc/pages/growthLoop/(OverviewTab, WeeklyReflectionTab, HabitTrackerTab, CycleReviewsTab + GrowthLoopTypes).Journey gate logic lives in both✅ Fixed March 2026 — gate conditions extracted toProtectedRoute.tsxandLayout.tsx— could be centralised.src/utils/journeyGates.ts. Both files now importcanAccessFutureStudio()andcanAccessGrowthLoop()from there.✅ Fixed April 2026 — both gate functions now includecanAccessWellnessProfileandcanAccessFinancialPlanrequired callers to add separatehasPaidAccesscheck.&& hasPaidAccess(profile)internally.Layout.tsxredundant checks removed.Rate limiter gap: high-frequency API abuse possible on authenticated endpoints.✅ Fixed April 2026 —middleware/rateLimiter.jscreated with per-useruserLimiter(120 req/min keyed onreq.user.uid). Applied toGET /meandPUT /meinusers.js. Global IP limiter raised to 800 req/15 min.