Skip to content

Commit d9f4239

Browse files
committed
feat(money-mirror): production hardening, schema drift, E2E; Linear VIJ-42 sync
- Perf: next/font, lazy Gemini insights path, WebVitalsReporter, viewport - Schema: db:upgrade, boot DDL, SCHEMA_DRIFT API hints, pg-errors tests - QA: Playwright smoke, launch checklist doc, PERFORMANCE/SCHEMA docs - Refactor: split useDashboardState for 300-line policy (url model, upload, initial load) - Linear: VIJ-42 (Done) production-readiness mirror; project-state + issue-010.json Made-with: Cursor
1 parent fe76a1d commit d9f4239

43 files changed

Lines changed: 1078 additions & 163 deletions

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: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,37 @@
11
# Changelog
22

3+
## 2026-04-06 — MoneyMirror: E2E documentation pass, `CODEBASE-CONTEXT` refresh, verification
4+
5+
**App:** `apps/money-mirror`
6+
7+
**What:** Refreshed [`apps/money-mirror/CODEBASE-CONTEXT.md`](apps/money-mirror/CODEBASE-CONTEXT.md) for post–issue-010 hardening: idempotent schema upgrades (`src/lib/schema-upgrades.ts`, `scripts/apply-schema-upgrades.ts`), boot-time DDL (`src/lib/run-schema-upgrade-on-boot.ts`, `src/instrumentation.ts`), Postgres error helpers (`src/lib/pg-errors.ts`) and `SCHEMA_DRIFT` JSON responses, `WebVitalsReporter`, Playwright (`playwright.config.ts`, `e2e/`). README **Docs** section links [`docs/PERFORMANCE-REVIEW.md`](apps/money-mirror/docs/PERFORMANCE-REVIEW.md) and [`docs/SCHEMA-DRIFT.md`](apps/money-mirror/docs/SCHEMA-DRIFT.md).
8+
9+
**Verification (local):** `npm run lint`, `npm run test` (81/81), `npx playwright install chromium`, `npm run test:e2e` (2/2, from `apps/money-mirror`); `npm run check:all` (repo root); `npm run db:upgrade` (success — Neon via `.env.local`).
10+
11+
**Learnings / corrections:** (1) **`db:upgrade` script shape** — an initial version used top-level `await`, which breaks `tsx`/`esbuild` execution; wrapping the script in `main()` fixed `npm run db:upgrade` (see earlier 2026-04-06 changelog entry). (2) **Schema drift vs “app bug”** — Neon DBs created before Phase 3 columns can throw Postgres `42703` (undefined column); API routes return `code: SCHEMA_DRIFT` with `SCHEMA_UPGRADE_HINT` so operators run `npm run db:upgrade` or rely on auto DDL on server boot (unless `MONEYMIRROR_SKIP_AUTO_SCHEMA=1`). (3) **No PM-pasted mistake list in this session** — additional bullets can be appended to `project-state.md` Decisions Log under the same date if needed.
12+
13+
---
14+
15+
## 2026-04-06 — MoneyMirror: `db:upgrade` + schema drift API hints
16+
17+
**App:** `apps/money-mirror`
18+
19+
**What:** Idempotent `npm run db:upgrade` (`scripts/apply-schema-upgrades.ts`, shared `applyIdempotentSchemaUpgrades` in `src/lib/schema-upgrades.ts`) adds `merchant_key` + partial index and statement label columns when missing. `GET /api/transactions` and `GET /api/insights/merchants` return `code: SCHEMA_DRIFT` with upgrade instructions on Postgres undefined-column errors; Transactions tab and `MerchantRollups` surface `detail` for that code. Dev dependency: `tsx`.
20+
21+
**Follow-up:** `runAutoSchemaUpgradeOnBoot` (`src/lib/run-schema-upgrade-on-boot.ts`) runs the same DDL from `instrumentation.ts` on Node server start when `DATABASE_URL` is set (opt out with `MONEYMIRROR_SKIP_AUTO_SCHEMA=1`). UI shows a single PM-friendly paragraph for `SCHEMA_DRIFT` (no duplicated title + hint). **Docs:** `docs/SCHEMA-DRIFT.md` (RCA + local verification).
22+
23+
**Fix:** `scripts/apply-schema-upgrades.ts` — wrap in `main()` (no top-level `await`) so `tsx`/`esbuild` can run `npm run db:upgrade`.
24+
25+
---
26+
27+
## 2026-04-06 — MoneyMirror: perf hardening, E2E smoke, launch checklist (post–issue-010)
28+
29+
**App:** `apps/money-mirror`
30+
31+
**What:** `next/font` (Inter + Space Grotesk); `WebVitalsReporter` + optional `NEXT_PUBLIC_POSTHOG_*`; viewport `maximumScale: 5`; Playwright smoke (`e2e/`, `test:e2e`); lazy Gemini on Insights (`/api/dashboard` fast path); scope-keyed dashboard refetch; dev-only transaction 500 `detail`; `dev:loopback` + README note for `uv_interface_addresses` dev noise. Docs: `docs/PERFORMANCE-REVIEW.md`, `experiments/results/production-launch-checklist-010.md`. **Linear:** ops follow-up comment on **VIJ-37** (does not reopen pipeline).
32+
33+
---
34+
335
## 2026-04-05 — MoneyMirror Phase 3 T4 (VIJ-41): facts-grounded AI coaching
436

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

apps/money-mirror/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,7 @@ next-env.d.ts
4242

4343
# Sentry Config File
4444
.env.sentry-build-plugin
45+
46+
# Lighthouse (generated locally; see docs/PERFORMANCE-REVIEW.md)
47+
/docs/lighthouse-report.report.html
48+
/docs/lighthouse-report.report.json

apps/money-mirror/CODEBASE-CONTEXT.md

Lines changed: 26 additions & 18 deletions
Large diffs are not rendered by default.

apps/money-mirror/README.md

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -51,22 +51,25 @@ cp .env.local.example .env.local
5151

5252
Fill in these values:
5353

54-
| Variable | Required | Description |
55-
| ------------------------- | -------- | ------------------------------------------------------------------- |
56-
| `DATABASE_URL` | Yes | Neon Postgres connection string |
57-
| `NEON_AUTH_BASE_URL` | Yes | Base URL for your Neon Auth project |
58-
| `NEON_AUTH_COOKIE_SECRET` | No | Optional only if Neon explicitly gives one for your project/runtime |
59-
| `GEMINI_API_KEY` | Yes | Google AI Studio API key |
60-
| `RESEND_API_KEY` | Yes | Resend API key |
61-
| `POSTHOG_KEY` | Yes | Server-side PostHog key |
62-
| `POSTHOG_HOST` | Yes | PostHog host URL |
63-
| `NEXT_PUBLIC_APP_URL` | Yes | Public app URL used in recap links |
64-
| `CRON_SECRET` | Yes | Shared secret for cron routes |
65-
| `NEXT_PUBLIC_SENTRY_DSN` | No | Client Sentry DSN — optional locally if you skip browser reporting |
66-
| `SENTRY_AUTH_TOKEN` | Yes\* | \*Required for production builds that upload source maps to Sentry |
67-
| `SENTRY_ORG` | No | Optional locally — used by Sentry CLI / webpack plugin for releases |
68-
| `SENTRY_PROJECT` | No | Optional locally — same as above |
69-
| `CI` | No | Optional CI build flag |
54+
| Variable | Required | Description |
55+
| ------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
56+
| `DATABASE_URL` | Yes | Neon Postgres connection string |
57+
| `NEON_AUTH_BASE_URL` | Yes | Base URL for your Neon Auth project |
58+
| `NEON_AUTH_COOKIE_SECRET` | No | Optional only if Neon explicitly gives one for your project/runtime |
59+
| `GEMINI_API_KEY` | Yes | Google AI Studio API key |
60+
| `RESEND_API_KEY` | Yes | Resend API key |
61+
| `POSTHOG_KEY` | Yes | Server-side PostHog key |
62+
| `POSTHOG_HOST` | Yes | PostHog host URL |
63+
| `NEXT_PUBLIC_POSTHOG_KEY` | No | Same key as `POSTHOG_KEY` — enables client `web_vital` (CWV) events |
64+
| `NEXT_PUBLIC_POSTHOG_HOST` | No | Defaults to `https://app.posthog.com` if unset |
65+
| `NEXT_PUBLIC_APP_URL` | Yes | Public app URL used in recap links |
66+
| `CRON_SECRET` | Yes | Shared secret for cron routes |
67+
| `NEXT_PUBLIC_SENTRY_DSN` | No | Client Sentry DSN — optional locally if you skip browser reporting |
68+
| `SENTRY_AUTH_TOKEN` | Yes\* | \*Required for production builds that upload source maps to Sentry |
69+
| `SENTRY_ORG` | No | Optional locally — used by Sentry CLI / webpack plugin for releases |
70+
| `SENTRY_PROJECT` | No | Optional locally — same as above |
71+
| `CI` | No | Optional CI build flag |
72+
| `MONEYMIRROR_SKIP_AUTO_SCHEMA` | No | Set to `1` to skip automatic idempotent DDL on server boot (`instrumentation.ts`); default is to run when `DATABASE_URL` is set |
7073

7174
### 3. Create Neon project and enable Neon Auth
7275

@@ -81,6 +84,18 @@ Fill in these values:
8184

8285
Run the full contents of [`schema.sql`](./schema.sql) against your Neon database.
8386

87+
**Already have a DB from before Phase 2 / Phase 3?** Your `transactions` table may be missing `merchant_key` (and related indexes), which breaks the Transactions tab and merchant insights. From this app directory, with `DATABASE_URL` in `.env.local`:
88+
89+
```bash
90+
npm run db:upgrade
91+
```
92+
93+
That runs idempotent `ALTER TABLE … ADD COLUMN IF NOT EXISTS` and `CREATE INDEX IF NOT EXISTS` statements (same as the tail of `schema.sql`). Safe to run multiple times.
94+
95+
**Automatic upgrades on server start:** When the Node server boots (`next dev` / `next start` / Vercel), MoneyMirror runs the same idempotent DDL once if `DATABASE_URL` is set and `MONEYMIRROR_SKIP_AUTO_SCHEMA` is not `1`. After pulling new code, **restart the dev server** so this runs; you usually do not need a separate migration step for missing `merchant_key` on Neon.
96+
97+
Full **root cause, verification steps, and opt-out** for schema drift: [`docs/SCHEMA-DRIFT.md`](./docs/SCHEMA-DRIFT.md).
98+
8499
Tables created:
85100

86101
- `profiles`
@@ -118,6 +133,19 @@ First-run failure looks like:
118133
- `Error: DATABASE_URL is required.`
119134
- `Error: NEON_AUTH_BASE_URL is required`
120135

136+
**Dev server: `uv_interface_addresses` / `getNetworkHosts` warning** — In some restricted environments Next may log an unhandled rejection while resolving the LAN URL; the app often still serves on `http://localhost:3000`. Use `npm run dev:loopback` to bind only to `127.0.0.1` and avoid that code path, or run the dev server outside a sandbox.
137+
138+
## Testing
139+
140+
| Command | What it runs |
141+
| ------------------ | -------------------------------------------------------------------------- |
142+
| `npm run test` | Vitest — API routes, libs, parsers |
143+
| `npm run test:e2e` | Playwright — builds, serves on port **3333**, smoke-tests `/` and `/login` |
144+
145+
First-time E2E setup: `npx playwright install chromium`. See [`docs/PERFORMANCE-REVIEW.md`](./docs/PERFORMANCE-REVIEW.md) for Lighthouse and performance notes.
146+
147+
Optional: set `NEXT_PUBLIC_POSTHOG_KEY` (same project as `POSTHOG_KEY`) to send **Core Web Vitals** (`web_vital` events) from the browser.
148+
121149
## API
122150

123151
### `POST /api/onboarding/complete`
@@ -179,7 +207,7 @@ Returns all processed statements for the authenticated user, sorted by creation
179207

180208
### `GET /api/dashboard`
181209

182-
Returns the full dashboard state for the authenticated user (Overview aggregates + generated advisories + optional `coaching_facts` Layer A JSON + Gemini-enriched `narrative` / `cited_fact_ids` on each advisory when AI succeeds).
210+
Returns the full dashboard state for the authenticated user (Overview aggregates + generated advisories + optional `coaching_facts` Layer A JSON). **Does not** call Gemini — responses are fast for Overview and Transactions. Rule-based advisory copy is returned immediately.
183211

184212
**Auth**: Neon Auth session cookie required.
185213

@@ -190,7 +218,7 @@ Returns the full dashboard state for the authenticated user (Overview aggregates
190218

191219
### `GET /api/dashboard/advisories`
192220

193-
Same query parameters as `GET /api/dashboard`. Returns `{ advisories, coaching_facts }` (same shapes as the parent dashboard payload).
221+
Same query parameters as `GET /api/dashboard`. Returns `{ advisories, coaching_facts }`. This endpoint runs the **Gemini coaching narrative** step (can take up to ~9s) and is what the **Insights** tab calls after the fast dashboard load.
194222

195223
**Auth**: Neon Auth session cookie required.
196224

@@ -345,6 +373,8 @@ Intentional error route to verify Sentry server-side capture (`SentryExampleAPIE
345373
## Docs
346374

347375
- [`docs/COACHING-TONE.md`](./docs/COACHING-TONE.md) — Coaching language guide: consequence-first nudge patterns, Layer A facts-only numerics, Gemini narrative guardrails, tone constraints for advisory copy.
376+
- [`docs/PERFORMANCE-REVIEW.md`](./docs/PERFORMANCE-REVIEW.md) — Performance review notes (fonts, lazy Insights path, Web Vitals, Lighthouse).
377+
- [`docs/SCHEMA-DRIFT.md`](./docs/SCHEMA-DRIFT.md) — Schema drift RCA, `SCHEMA_DRIFT` API behavior, `db:upgrade` vs boot-time DDL.
348378

349379
## Current scope
350380

0 commit comments

Comments
 (0)