Issue: 011
Project: MoneyMirror (apps/money-mirror)
Linear: Parent VIJ-43; child epics VIJ-44 (P4-A), VIJ-45 (P4-E), VIJ-46 (P4-G), VIJ-47 (P4-F), VIJ-48 (P4-D), VIJ-49 (P4-B), VIJ-51 (P4-C), VIJ-50 (P4-H). Plan snapshot. Prior Phase 3 cycle: VIJ-37 (issue-010) complete.
Stage: plan
Status: approved for execution (start P4-A / VIJ-44)
Date: 2026-04-06
Phase 4 completes the MoneyMirror wedge for India: statement-native truth at merchant and UPI-handle granularity, deterministic bad-pattern signals in the advisory engine, monetization and Product Hunt readiness, proactive channels (email first; WhatsApp spike parallel), ingestion trust for heavy testers, optional facts-only chat, and hardening (per-user rate limits on heavy reads, Gemini HTTP-abort follow-ups). Phase 3 delivered unified scope, transactions, merchant rollups, and facts-grounded coaching; Phase 4 extends those surfaces without relaxing ownership checks, facts-only numerics for generative copy, or single emission source for PostHog.
Canonical stack (unchanged): Next.js 16 App Router, Neon Auth (email OTP), Neon Postgres, Gemini 2.5 Flash, Resend, PostHog (POSTHOG_KEY / POSTHOG_HOST server-side), Sentry. Server-enforced ownership in route handlers (not RLS).
Epic execution order: P4-A → P4-E → P4-G → P4-F → P4-D → P4-B → P4-C → P4-H (see exploration-011.md).
Increase trust, depth of engagement (transactions, insights, advisories), and repeat statement upload (North Star proxy continuity from issue-009/010) by making where money went legible at merchant / UPI level, surfacing high-signal leaks (micro-UPI, recurring, CC min-due), and establishing a paid / launch story without hallucinated amounts.
- Primary: Gen Z Indians (₹20K–₹80K/month), UPI-native, multi-account, bank + credit card PDF uploads.
- Secondary: PM/owner — Linear parent VIJ-43 + epics P4-A–P4-H; production hygiene per production-launch-checklist-010.md.
- Dashboard — User sees merchant/UPI-aware labels (and optional rename) consistent across Overview, Transactions, Insights.
- Insights / feed — New bad-pattern advisories (micro-UPI, recurring noise, CC risk) with tap-through to scoped Transactions.
- Upgrade / paywall — After Mirror moment or key value action, user may see upgrade intent or paywall UI (experiment slice; payment integration scoped in manifest).
- IA — URL-backed tab selection and month-over-month compare (deferred Phase 3 T5–T6) ship as P4-F.
- Proactive — Richer weekly recap; optional WhatsApp opt-in + sandbox; optional web push for high-signal alerts.
- Ingestion — Queue/retry UX and golden-PDF regression for trust at scale.
- Chat (optional) — User asks questions; answers cite Layer A facts + bounded txn context only.
- Hardening — Heavy APIs rate-limited per user; Gemini calls per existing timeout/abort policy.
| Epic | In scope | Out of scope (explicit) |
|---|---|---|
| P4-A | UPI handle extraction/display; user_merchant_aliases (rename map); optional async Gemini re-label with persisted audit (merchant_key → suggested label, confidence); reduce “other” dominance |
Real-time bank/SMS aggregation; blocking parse on Gemini |
| P4-E | Micro-UPI totals, recurring pattern, CC min-due / risk flags in advisory-engine.ts; new triggers + tap-through |
Investment product recommendations; non-deterministic thresholds without tests |
| P4-G | Pricing hypothesis; paywall prompt / entitlements flag; Product Hunt checklist + landing hooks | Full payment provider integration unless scoped in execute-plan slice |
| P4-F | URL-backed primary tabs / shareable ?tab=; month-over-month compare view + copy |
Desktop-only polish beyond mobile-first PWA |
| P4-D | Richer Resend templates; WhatsApp spike (sandbox, opt-in); web push optional behind permission UX | Full TRAI/compliance program; bulk SMS |
| P4-B | Upload queue UX; limits policy clarity; golden PDF fixtures + CI/regression hook | Unlimited uploads bypassing abuse controls |
| P4-C | POST /api/chat (or similar) grounded in buildLayerAFacts + txn subset; rate limit; citations UI |
Open-ended chat over raw PDF text; client-only “answers” |
| P4-H | Per-user rate limits on authenticated heavy GETs; document/follow-up true HTTP abort for Gemini |
Global DDoS edge product |
- UPI identity — Extract VPA/handle to a dedicated column
transactions.upi_handle TEXT(nullable) when parse/description matches UPI patterns; keepdescriptionraw. Display uses alias map → enriched label →merchant_key→ description. - Merchant rename — User wins:
user_merchant_aliases (user_id, merchant_key UNIQUE, display_label)overrides display everywhere rollups read labels. - Gemini re-label — Never on critical path for upload; batch/cron or on-demand enrichment writes to
merchant_label_suggestions(or merges into alias after user confirm). Storesource,confidence,model,created_atfor audit. - Paywall — First slice = feature flag + server entitlements column on
profilesOR separateentitlementstable; full Stripe/Razorpay deferred unless PM expands scope in execute-plan. - Chat context — Server sends Layer A JSON + limited txn list (cap count + date range); prompt forbids new numbers; validate structured output like existing coaching narrative path.
| Success metric | User flow (where measured) | Telemetry (single source) |
|---|---|---|
| North Star proxy — repeat statement upload ≤60d | Second successful upload after first | Existing statement_parse_success + cohort in /metric-plan (finalize in metric-plan stage) |
| Merchant clarity engagement | User opens Insights merchant row or Transactions from merchant | merchant_rollup_clicked (existing) |
| Bad-pattern usefulness | User taps new advisory card → Transactions | bad_pattern_advisory_shown, bad_pattern_advisory_clicked (server or client; one source pair) |
| Upgrade intent | User sees paywall prompt after Mirror / key action | paywall_prompt_seen, upgrade_intent_tapped |
| Chat adoption | User sends chat message | chat_query_submitted, chat_response_rendered |
| Proactive reach | User opens recap email / WhatsApp / push | weekly_recap_email_sent (existing); add whatsapp_opt_in_completed, push_subscription_granted if implemented |
| API abuse resistance | Heavy dashboard loads | rate_limit_hit (existing pattern) on throttled routes |
- Dashboard — Preserve ScopeBar; ensure merchant labels and UPI handles appear in Transactions rows, Merchant rollups, and Overview where merchant text is shown.
- P4-F — URL-synced primary tab (
?tab=overview|transactions|insights) and deep links; Compare subview or toggle for month-vs-month with clear scope copy. - P4-C — Chat as drawer or bottom sheet on mobile; citations list (fact ids + txn refs); rate limit messaging.
- Rename merchant — Insights or merchant row → “Rename” → modal → save →
POST /api/.../merchant-alias→ immediate UI update; optional “Use suggestion” if Gemini suggestion exists. - Bad-pattern advisory — Feed card → tap “Review transactions” → Transactions with query preset (filters for micro-UPI or merchant_key); AbortController on fetch per ui-standards.
- Paywall prompt — Shown after defined trigger (e.g. Mirror card viewed); dismiss or upgrade CTA; try/catch on any local persistence.
- Txn row — Show
upi_handlechip and effective display label (alias > suggestion > merchant_key). - MerchantRollups — Use resolved label; show “Other” reduction only when backed by data.
- AdvisoryFeed — New card variants for P4-E triggers; loading/error states.
- ChatPanel — Message list, input, citation accordion, daily limit banner.
- Focus traps in modals; aria labels on merchant rename and chat send.
- Monolith
apps/money-mirror— new routes undersrc/app/api/; libraries undersrc/lib/. - Auth —
getSessionUser()on every new endpoint; no trusting bodyuserId. - Merchant pipeline — Extend merchant-normalize.ts, persist-statement path; optional
/api/cron/merchant-enrichor internal job after upload. - Advisories — Extend advisory-engine.ts with deterministic SQL aggregates for micro-UPI bands, recurring detection, CC min-due vs income (reuse statement/card fields).
- Chat — New route: load facts via existing Layer A builder; attach redacted txn list; Gemini structured answer + citations; ≤9s timeout aligned with engineering lessons.
- Proactive — Extend weekly recap worker and templates under existing cron patterns; WhatsApp via HTTP API stub (env-gated).
- Rate limits — Middleware or per-route token bucket keyed by
user_idfor listedGETroutes (P4-H).
| Method | Path | Purpose |
|---|---|---|
| GET/POST | /api/merchant-alias or /api/merchants/alias |
CRUD user alias for merchant_key |
| POST | /api/merchants/suggest-accept |
Optional accept Gemini suggestion |
| GET | /api/dashboard (extend) |
Include resolved merchant display names where relevant |
| POST | /api/chat |
Facts-grounded chat turn |
| POST | /api/webhooks/whatsapp |
Stub/inbound if spike needs it |
| GET | /api/compare-months |
MoM aggregates for P4-F (or folded into dashboard) |
Telemetry: Fire-and-forget PostHog; no await flush on user-facing routes.
- Parse path sets
merchant_key,upi_handlewhen detected. - Rollups JOIN
user_merchant_aliasesfor display label. - Advisory engine runs after txn load; inserts into
advisory_feedwith newtriggerenum values (document in code).
- Neon only; migrations via
schema.sql+ idempotent ALTERs; align with schema-upgrades.
DB: Neon Postgres (existing).
| Object | Purpose |
|---|---|
transactions.upi_handle |
Nullable TEXT; extracted VPA/handle for display and rollup subgrouping |
user_merchant_aliases |
(user_id, merchant_key) PK or UNIQUE; display_label TEXT NOT NULL; updated_at |
merchant_label_suggestions |
Optional: user_id, merchant_key, suggested_label, confidence NUMERIC, source TEXT, created_at; partial unique per user+key |
profiles |
Optional: entitlements JSONB or plan TEXT for paywall MVP |
| Indexes | (user_id, upi_handle) partial WHERE NOT NULL; alias FK/index on user_id |
- Unchanged:
transactions.statement_id→statements.id; all queries scoped byuser_id.
- New events listed in
manifest-011.jsonposthog_eventsarray. - Single emission source per business event (CLAUDE anti-pattern §9).
- Workers/cron: error-path + completion events per existing patterns.
See manifest-011.json for phased tasks, dependencies, verification commands, and Linear epic mapping.
-
transactions.upi_handlepopulated where parse detects UPI/VPA; migration inschema.sql+ upgrades path. - UI shows handle and/or resolved display label on txn rows and merchant rollups.
- User can rename a
merchant_key; persisted inuser_merchant_aliases; rollups and Transactions use it. - Optional Gemini re-label does not block parse; suggestions stored with audit fields; user can accept/reject.
- PostHog:
merchant_alias_saved,merchant_suggestion_accepted(if suggestions ship) — single source each.
- At least three new deterministic signals (e.g. micro-UPI month total, recurring noise, CC min-due risk) with tests.
- New advisories appear in feed with tap-through to Transactions + matching filters.
-
bad_pattern_advisory_*events wired per metric table.
- Paywall prompt or gating behind
plan/entitlementswith safe default (all current users grandfathered). -
paywall_prompt_seen/upgrade_intent_tappedemitted per spec. - Product Hunt checklist references landing, demo asset hooks — doc or README section.
- URL-backed dashboard tabs (
?tab=sync + shareable state). - Month-over-month compare for scope-aligned totals (with paisa rounding rules documented).
- Richer email recap content (within Resend limits); no PII leakage in logs.
- WhatsApp: opt-in + sandbox send OR documented stub with env flag off in prod default.
- Web push: optional; permission UX only if implemented.
- Upload queue or retry UX when parse fails/times out; user-visible status.
- Golden PDF fixtures in repo + test or CI step documented in README.
- Chat API returns answers only from facts + bounded txn evidence; structured validation; coaching tone per COACHING-TONE.md.
- Daily rate limit per user; 429 with clear message.
- Per-user rate limits on agreed heavy
GETroutes (list in execute-plan). - Gemini: follow-up issue or inline note for true HTTP AbortController if still missing.
| Risk | Mitigation |
|---|---|
| Gemini re-label latency/cost | Async only; batch; cache suggestions |
| WhatsApp vendor delay | Email first; sandbox spike; opt-in |
| Chat token cost | Rate limits; small context; facts-only |
| Paywall backlash | Grandfather existing users; soft prompt first |
| Parser regression | Golden PDFs; no issuer change without fixture |
| Regulatory / tone | COACHING-TONE + disclaimers on chat and WhatsApp |
Run /execute-plan starting with P4-A (see manifest). Then /linear-sync plan (mandatory checkpoint) to sync PRD + child epics to Linear VIJ-43 project.