Skip to content

Commit 180b36e

Browse files
committed
feat(money-mirror): prepare issue-012 production release candidate
Bundle the latest local Money Mirror enhancements and pipeline artifacts into a single branch for production validation, including dashboard clarity UX, frequency insights, guided review flows, proactive touchpoints, and release gating documentation. Made-with: Cursor
1 parent 93306c7 commit 180b36e

114 files changed

Lines changed: 9925 additions & 441 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,116 @@
11
# Changelog
22

3+
## 2026-04-07 — peer-review-012 blocker fixes (issue-012)
4+
5+
- **Privacy contract enforcement (server):** [`apps/money-mirror/src/app/api/guided-review/outcome/route.ts`](apps/money-mirror/src/app/api/guided-review/outcome/route.ts) now forces `commitment_text = NULL` whenever `dismissed = true`, regardless of client payload. Telemetry uses the server-derived value. Regression test added at [`__tests__/route.test.ts`](apps/money-mirror/src/app/api/guided-review/outcome/__tests__/route.test.ts).
6+
- **Cluster rollup truthfulness:** New helper `fetchClusterMerchantAggregates()` in [`dashboard-helpers.ts`](apps/money-mirror/src/lib/dashboard-helpers.ts) runs a full-scope SQL aggregate bound by `merchant_key = ANY(ALL_CLUSTER_MERCHANT_KEYS)` (no LIMIT). [`frequency-clusters/route.ts`](apps/money-mirror/src/app/api/insights/frequency-clusters/route.ts) now feeds cluster rollups from this query while keeping the LIMIT-30 list only as the UI top-merchants preview. Fixes the issue-010-class anti-pattern where cluster totals undercounted whenever a clustered merchant fell outside the top 30. New test in [`frequency-clusters/__tests__/route.test.ts`](apps/money-mirror/src/app/api/insights/frequency-clusters/__tests__/route.test.ts) proves cluster totals include merchants absent from the top-N sample.
7+
- **Scope-keyed SLA timers:** [`DashboardClient.tsx`](apps/money-mirror/src/app/dashboard/DashboardClient.tsx) now resets `mountTimeRef`, `dashboardReadyFiredRef`, and `advisoryReadyFiredRef` whenever `dashboardScopeKey` changes, so `dashboard_ready_ms` and `time_to_first_advisory_ms` re-fire per scope rather than only on first mount.
8+
- **Prompt autopsy applied:** [`agents/backend-engineer-agent.md`](agents/backend-engineer-agent.md) §5 (server-authoritative consent flags), [`agents/backend-architect-agent.md`](agents/backend-architect-agent.md) checklist item 15 (full-scope rollup truthfulness), [`agents/frontend-engineer-agent.md`](agents/frontend-engineer-agent.md) §6 (scope-keyed SLA timers).
9+
- **Verification:** `npm test` 159/159 ✅, `npm run lint` ✅, `npm run build` ✅ (apps/money-mirror).
10+
- **Source:** [`experiments/results/peer-review-012.md`](experiments/results/peer-review-012.md) (BLOCKED → fixes for both blockers + medium).
11+
12+
---
13+
14+
## 2026-04-07 — `/linear-sync plan` issue-012
15+
16+
- **Linear:** Project state **Planned**; root [**VIJ-52**](https://linear.app/vijaypmworkspace/issue/VIJ-52/issue-012-gen-z-clarity-loop-emotional-ux-frequency-perf-slas)**Todo** (Planning); document [issue-012 Plan Snapshot](https://linear.app/vijaypmworkspace/document/issue-012-plan-snapshot-7032c79bc5b0); child tasks **VIJ-53****VIJ-64** from [`experiments/plans/manifest-012.json`](experiments/plans/manifest-012.json).
17+
- **Repo:** [`experiments/linear-sync/issue-012.json`](experiments/linear-sync/issue-012.json) task map + `manifest-012.json` `linear.tasks` identifiers.
18+
19+
---
20+
21+
## 2026-04-07 — `/create-plan` issue-012 (Gen Z clarity loop)
22+
23+
- **Artifacts:** [`experiments/plans/plan-012.md`](experiments/plans/plan-012.md), [`experiments/plans/manifest-012.json`](experiments/plans/manifest-012.json)
24+
- **Themes:** T0 emotional UX + performance-to-insight SLAs; T1 frequency-first + deterministic merchant clusters; T2 guided review + proactive/recap copy; schema addition **`guided_review_outcomes`** (opt-in commitment storage).
25+
- **Linear:** root [**VIJ-52**](https://linear.app/vijaypmworkspace/issue/VIJ-52/issue-012-gen-z-clarity-loop-emotional-ux-frequency-perf-slas); **`/linear-sync plan`** checkpoint to sync PRD + child tasks.
26+
- **Next pipeline step:** `/execute-plan` (start **phase-t0**).
27+
28+
---
29+
30+
## 2026-04-07 — Issue Created: issue-012
31+
32+
- **Type**: Enhancement
33+
- **Title**: MoneyMirror — Gen Z clarity loop: emotional UX, frequency-first insights, and performance-to-insight SLAs
34+
- **App**: apps/money-mirror
35+
- **Status**: Discovery
36+
- **Linear**: root [VIJ-52](https://linear.app/vijaypmworkspace/issue/VIJ-52/issue-012-gen-z-clarity-loop-emotional-ux-frequency-perf-slas), project [issue-012 — Gen Z clarity loop](https://linear.app/vijaypmworkspace/project/issue-012-gen-z-clarity-loop-emotional-ux-frequency-perf-slas-1bbe50903d79)
37+
- **Source**: `.cursor/plans/money_mirror_10x_issue_34a61725.plan.md`
38+
39+
---
40+
41+
## 2026-04-07 — issue-011 QA harness (env, tests, README)
42+
43+
- **App:** `apps/money-mirror`
44+
- **Paywall visibility for local QA:** `.env.local.example` sets `NEXT_PUBLIC_PAYWALL_PROMPT_ENABLED=1` (copy still opt-in per deploy); local `.env.local` can mirror for manual Overview testing (gitignored).
45+
- **Regression script:** `npm run test:issue-011` runs Vitest for P4 advisories, MoM compare, rate limits, user plan, merchant normalize, `POST /api/chat`, compare-months API, and new `__tests__/api/proactive.test.ts` (WhatsApp opt-in stub + push subscription).
46+
- **README:** Testing table + short manual QA note for P4-E thresholds (points to `bad-pattern-signals.ts`).
47+
48+
---
49+
50+
## 2026-04-06 — `/execute-plan` issue-011: P4-D/B/C/H completion (proactive, ingestion trust, chat, hardening)
51+
52+
- **App:** `apps/money-mirror`
53+
- **Proactive channels (P4-D):** added `POST /api/proactive/whatsapp-opt-in` (auth required, env-gated provider relay, telemetry-only stub fallback) and `POST /api/proactive/push-subscription`.
54+
- **Ingestion trust (P4-B):** Upload panel now shows an explicit **Retry upload** CTA on parse errors; README adds golden PDF regression runbook.
55+
- **Facts-only chat (P4-C):** added `POST /api/chat` with dashboard-scope parsing, Layer A facts grounding, bounded transaction context, strict JSON response parsing, citation validation, and daily chat rate limiting (`chat_query_submitted`, `chat_response_rendered`, `chat_rate_limited`).
56+
- **Hardening (P4-H):** introduced shared `src/lib/rate-limit.ts`; enforced per-user heavy-read limits on `GET /api/dashboard`, `GET /api/transactions`, and `GET /api/insights/merchants`; emits `rate_limit_hit`.
57+
- **Docs/env/tests:** `.env.local.example` now includes `WHATSAPP_API_URL` / `WHATSAPP_API_TOKEN`; README API + analytics tables updated. Added tests: `__tests__/api/chat.test.ts`, `src/lib/__tests__/rate-limit.test.ts`. Validation: `npm --prefix apps/money-mirror test` (105/105), `npm --prefix apps/money-mirror run lint`, `npm --prefix apps/money-mirror run build` (Sentry sourcemap upload warning under sandbox only).
58+
59+
---
60+
61+
## 2026-04-06 — `/execute-plan` issue-011: P4-G (VIJ-46) monetization + Product Hunt hooks
62+
63+
- **App:** `apps/money-mirror`
64+
- **Schema:** `profiles.plan` (`free` \| `pro`, default `free`); idempotent DDL in `schema.sql` + `schema-upgrades.ts`.
65+
- **API:** `GET /api/dashboard` and `POST /api/statement/parse` responses include `plan`; `normalizeUserPlan` in `src/lib/user-plan.ts`.
66+
- **UI:** `PaywallPrompt` on Overview after the Money Mirror block is visible (IntersectionObserver) when `NEXT_PUBLIC_PAYWALL_PROMPT_ENABLED=1`; dismiss stored in `localStorage`.
67+
- **Telemetry:** client `paywall_prompt_seen`, `upgrade_intent_tapped` via `posthog-browser.ts` (requires `NEXT_PUBLIC_POSTHOG_KEY`).
68+
- **Docs:** README Product Hunt section + analytics table rows; `.env.local.example` documents `NEXT_PUBLIC_PAYWALL_PROMPT_ENABLED`.
69+
- **Tests:** Vitest includes `user-plan.test.ts`. **Next (issue-011):** P4-F / VIJ-47 per `manifest-011.json`.
70+
71+
---
72+
73+
## 2026-04-06 — `/execute-plan` issue-011: P4-E (VIJ-45) bad-pattern detection
74+
75+
- **App:** `apps/money-mirror`
76+
- **Advisories:** `MICRO_UPI_DRAIN`, `REPEAT_MERCHANT_NOISE`, `CC_MIN_DUE_INCOME_STRESS` in `advisory-engine.ts` (thresholds in `bad-pattern-signals.ts`); dashboard aggregates extended in `dashboard-unified.ts` / `dashboard-legacy.ts` (micro-UPI sums, top repeat `merchant_key`, CC min-due vs scope).
77+
- **API:** `GET /api/transactions?upi_micro=1` filters small UPI debits (≤₹500 with VPA); `transactions_filter_applied` includes `upi_micro` in `filter_types` when set.
78+
- **UI:** `AdvisoryFeed` CTA → `/dashboard?tab=transactions` with scope-preserving `merchant_key` / `upi_micro`; `TxnFilterBar` banner for micro-UPI filter.
79+
- **Telemetry:** `bad_pattern_advisory_shown` / `bad_pattern_advisory_clicked` via `posthog-browser.ts` (requires `NEXT_PUBLIC_POSTHOG_KEY`). Layer A facts: `micro_upi_debit_paisa`, `repeat_merchant_noise_paisa`, `cc_minimum_due_income_ratio`.
80+
- **Tests:** 92 passing. **Next (issue-011):** P4-G / VIJ-46 per `manifest-011.json`.
81+
82+
---
83+
84+
## 2026-04-06 — `/execute-plan` issue-011: P4-A (VIJ-44) merchant + UPI visibility
85+
86+
- **App:** `apps/money-mirror`
87+
- **Schema:** `transactions.upi_handle`; tables `user_merchant_aliases`, `merchant_label_suggestions` (+ indexes); idempotent DDL in `schema.sql` and `src/lib/schema-upgrades.ts`.
88+
- **Parse:** `extractUpiHandle` + persist on insert (`persist-statement.ts`); `merchant-normalize` exports `formatMerchantKeyForDisplay`.
89+
- **API:** `GET|POST|DELETE /api/merchants/alias`, `POST /api/merchants/suggest-accept`, `GET /api/cron/merchant-enrich` (weekly cron in `vercel.json`); `GET /api/transactions` and merchant rollups join aliases; rollups return `display_label` + optional Gemini suggestion fields.
90+
- **UI:** `TxnRow` UPI chip + alias-aware label; `MerchantRollups` rename modal + “Use AI suggestion”.
91+
- **Telemetry:** `merchant_alias_saved`, `merchant_suggestion_accepted` (server-side). Tests: 87 passing.
92+
- **Docs:** `apps/money-mirror/README.md` updated (endpoints, analytics, schema). **Next (issue-011):** P4-E / VIJ-45 per `manifest-011.json`.
93+
94+
---
95+
96+
## 2026-04-06 — `/create-plan` issue-011 (MoneyMirror Phase 4)
97+
98+
- **Artifacts:** [`experiments/plans/plan-011.md`](experiments/plans/plan-011.md) (PRD, UX, architecture, DB, AC per epic P4-A–P4-H); [`experiments/plans/manifest-011.json`](experiments/plans/manifest-011.json) (phases, tasks, `posthog_events`, env vars).
99+
- **Linear:** `/linear-sync plan` — project **Planned**; [issue-011 Plan Snapshot](https://linear.app/vijaypmworkspace/document/issue-011-plan-snapshot-f8ddb7ff33ef); child epics **VIJ-44** (P4-A) through **VIJ-51** (P4-C), **VIJ-50** (P4-H); root **VIJ-43** updated. Sync map [`experiments/linear-sync/issue-011.json`](experiments/linear-sync/issue-011.json).
100+
- **State:** [`project-state.md`](project-state.md) — stage `execute-plan`, quality gate `create_plan` done; next `/execute-plan` from **VIJ-44**.
101+
102+
---
103+
104+
## 2026-04-06 — Issue Created: issue-011
105+
106+
- **Type**: Enhancement
107+
- **Title**: MoneyMirror Phase 4 — Merchant-native visibility, proactive coaching, and growth readiness
108+
- **App**: apps/money-mirror
109+
- **Status**: Discovery
110+
- **Linear**: Project **issue-011 — MoneyMirror Phase 4 — merchant visibility & growth**; root [**VIJ-43**](https://linear.app/vijaypmworkspace/issue/VIJ-43/issue-011-moneymirror-phase-4-merchant-native-visibility-proactive)
111+
112+
---
113+
3114
## 2026-04-06 — MoneyMirror: E2E documentation pass, `CODEBASE-CONTEXT` refresh, verification
4115

5116
**App:** `apps/money-mirror`

agents/analytics-agent.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,7 @@ Every feature must have measurable outcomes.
149149
Avoid vanity metrics.
150150

151151
Prioritize metrics tied to user value.
152+
153+
**Canonical event dictionary is required**: Every metric-plan output must include a final table mapping each metric to (1) canonical implementation event name, (2) single authoritative emitter (client or server), and (3) required properties. Event aliases or intent labels that do not match implemented event IDs must be marked as non-canonical and excluded from final KPI calculations.
154+
155+
# Added: 2026-04-07 — MoneyMirror issue-012

agents/backend-architect-agent.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,19 +251,27 @@ Before finalizing the architecture, answer all of the following. Any gap must be
251251

252252
# Added: 2026-04-04 — MoneyMirror Phase 2
253253

254-
15. **Financial headline metrics (aggregates vs lists)**: For any finance dashboard, advisory pipeline, or AI facts layer:
254+
15. **Full-scope rollup truthfulness**: For any derived financial rollup or cluster shown with exact counts or currency totals, specify a full-scope SQL aggregation strategy.
255+
→ LIMIT-capped merchant or transaction lists may drive UI previews, but they must never be reused as the source of displayed rollup totals.
256+
→ If cluster/group membership is a static set (e.g., curated keys), bound the aggregate query with `WHERE col = ANY(<set>)` instead of LIMIT.
257+
→ If the rollup is dynamic, compute it in SQL with `GROUP BY` over the entire scope and present the top-N as a separate query.
258+
→ A spec that derives "exact" totals from a top-N sample is a blocking gap — exact-looking numbers must be exact.
259+
260+
# Added: 2026-04-07 — peer-review-012 fix
261+
262+
16. **Financial headline metrics (aggregates vs lists)**: For any finance dashboard, advisory pipeline, or AI facts layer:
255263
→ The plan must state that totals, category sums, and inputs to rules/AI are computed from **database aggregates** over the full user scope (`SUM` / `COUNT` with the same filters as scope), **not** from `LIMIT`-capped row scans.
256264
→ List/pagination queries for UI tables are separate from aggregate queries for headline numbers — never reuse the list query result as the source of summed totals.
257265

258266
# Added: 2026-04-05 — MoneyMirror Phase 3 (issue-010)
259267

260-
16. **Batch repair / backfill termination**: For any maintenance route that fixes nullable derived fields in batches (cursor + loop):
268+
17. **Batch repair / backfill termination**: For any maintenance route that fixes nullable derived fields in batches (cursor + loop):
261269
→ Document **termination proof**: cursor advances monotonically; rows that cannot be processed in one pass (e.g., normalization returns null permanently) are skipped or marked so they are not re-selected forever.
262270
→ "Process until no rows" without poison-row handling is a blocking omission.
263271

264272
# Added: 2026-04-05 — MoneyMirror Phase 3 (issue-010)
265273

266-
17. **Heavy authenticated read APIs**: For any authenticated endpoint that scans large row sets, runs expensive `GROUP BY`, or could be abused by rapid UI actions:
274+
18. **Heavy authenticated read APIs**: For any authenticated endpoint that scans large row sets, runs expensive `GROUP BY`, or could be abused by rapid UI actions:
267275
→ State an explicit strategy: pagination/cursor guarantees, per-user rate limits, query caps, or an explicit **MVP / trusted-client** assumption with documented risk acceptance.
268276
→ Auth + ownership alone are not sufficient when the query is O(n) in user data.
269277
# Added: 2026-04-05 — MoneyMirror Phase 3 (issue-010)

agents/backend-engineer-agent.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ database errors
109109

110110
Explain how errors are handled.
111111

112+
**Privacy / consent flag enforcement (server-authoritative):** When a route accepts a privacy or consent flag (for example `dismissed`, `opt_in`, `save_preference`) alongside optional free text, the server must enforce the contract itself by nullifying or rejecting the text whenever the flag says it should not be stored. Never rely on the client payload to preserve privacy boundaries — derive the persisted value from the flag on the server, then use that derived value in both the DB write and any telemetry properties.
113+
112114
---
113115

114116
## 6 Security Considerations
@@ -179,3 +181,7 @@ Experiment Integrity & Telemetry: Ensure cryptographic salts for A/B testing are
179181
Infra gaps discovered at `/deploy-check` are Backend Engineer failures. Ship infra, not just code.
180182

181183
# Added: 2026-04-03 — Shift-left infra validation (issue-009 postmortem pattern)
184+
185+
**Optional foreign IDs on write routes**: For any write endpoint that accepts an optional entity ID (`statement_id`, `order_id`, `session_id`, etc.), validate shape and verify authenticated ownership before any insert/update. If the ID is not owned by the caller, fail closed (`404` or `403`). Add at least one negative test proving cross-user IDs are rejected.
186+
187+
# Added: 2026-04-07 — MoneyMirror issue-012

agents/code-review-agent.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@ For every API route that writes a parent record followed by child records:
151151

152152
# Added: 2026-04-05 — MoneyMirror Phase 3 (issue-010)
153153

154+
**Aggregate-to-detail integrity**: For any UI/API drill-through launched from an aggregate row (cluster/category/rollup), verify the downstream filter semantics preserve the full aggregate scope. If implementation maps aggregate navigation to a single representative row, flag as **HIGH**.
155+
156+
**Completion on non-2xx guard**: For any user flow that displays a completion/success state after a mutation request, verify success is gated on explicit HTTP success (`response.ok` or equivalent). If non-2xx can still show success, flag as **HIGH**.
157+
158+
# Added: 2026-04-07 — MoneyMirror issue-012
159+
154160
---
155161

156162
## 5 Performance Risks

agents/deploy-agent.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ performance metrics
9595

9696
# Added: 2026-04-05 — MoneyMirror Phase 3 (issue-010)
9797

98+
**ENV optionality source of truth**: Treat `.env.local.example` inline annotations as canonical. A variable is optional only when explicitly marked near that key (for example `# Optional ...` adjacent to the variable). If optional behavior exists in code but annotation is missing, require updating `.env.local.example` in the same cycle.
99+
100+
**Schema verification fallback**: If MCP schema verification is unavailable (auth/tool outage), attempt direct DB verification using the app's configured DB client and `DATABASE_URL` before blocking. Record the exact `information_schema.tables` evidence in deploy-check output.
101+
102+
# Added: 2026-04-07 — MoneyMirror issue-012
103+
98104
---
99105

100106
## 5 Rollback Plan

agents/frontend-engineer-agent.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ lazy loading
120120
loading states
121121
error handling
122122

123+
**Scope-keyed SLA timers:** When a feature introduces product timing SLAs tied to route params or dashboard scope (e.g., `dashboard_ready_ms`, `time_to_first_advisory_ms`), reset the timing origin whenever the canonical scope changes. A one-time measurement on initial mount is insufficient for scope-driven dashboards — emit per-scope timings by keying the reset effect on the scope identifier and clearing one-shot fired refs along with the timer origin. (Added 2026-04-07 — peer-review-012 fix)
124+
123125
---
124126

125127
# Output Format
@@ -160,6 +162,12 @@ Browser Storage & Network Safety: Always wrap `JSON.parse` of `localStorage`/`se
160162

161163
# Added: 2026-04-05 — MoneyMirror Phase 3 (issue-010)
162164

165+
**Aggregate drill-through fidelity**: When a user taps an aggregate UI item (cluster/category/rollup), the downstream URL/API filter must preserve the full aggregate scope (`merchant_keys[]`, cluster filter, or equivalent). Never substitute a single representative row (`items[0]`) as the drill-through target.
166+
167+
**Completion-state correctness**: For mutating actions, transition UI to success only when the HTTP response is explicitly successful (`response.ok` or equivalent contract). Non-2xx responses must keep the user in-flow and surface a retryable error state; never show completion on failed persistence.
168+
169+
# Added: 2026-04-07 — MoneyMirror issue-012
170+
163171
**Dashboard and scope loads**: Treat main data loads triggered by scope changes like search — use `AbortController`, abort the prior request when issuing a new one, and ignore `AbortError` so stale responses cannot overwrite the UI.
164172

165173
# Added: 2026-04-05 — MoneyMirror Phase 3 (issue-010)

0 commit comments

Comments
 (0)