Post-cycle hotfix — production HTTP 500 on GET /api/dashboard triggered by Gmail sync introducing a gmail_sync statement type that Zod schema validation did not accept.
Root cause: layerAFactsSchema in coaching-facts.ts used z.enum(['bank_account', 'credit_card']) — 'gmail_sync' was absent. After a Gmail sync run, fetchDashboardLegacy (no statement_type filter) would pick the most-recently-created gmail_sync statement. buildLayerAFacts then called layerAFactsSchema.parse({ statement_type: 'gmail_sync' }), throwing a ZodError caught by the route's try/catch and returned as HTTP 500.
Fixes applied:
coaching-facts.ts:31— Added'gmail_sync'tolayerAFactsSchemastatement_typeenum. (crash fix)statements.ts:1— Added'gmail_sync'to theStatementTypeTypeScript union. (type correctness)dashboard-legacy.ts:50— AddedAND s.statement_type != 'gmail_sync'to thestatementId=nullWHERE clause so the legacy path never selects Gmail synthetic statements. (behavior fix)schema-upgrades.ts— Added DROP/ADD ofstatements_statement_type_checkconstraint to include'gmail_sync'for new DB deployments. (schema hygiene)gmail/run-sync.ts:173— FixedinsertedCount > 0 ? 'ok' : 'ok'copy-paste bug to'ok' : 'partial'so all-skip sync runs are logged correctly. (observability fix)
Post-cycle ship (outside issue-012 pipeline) — Gmail is now the primary ingestion path.
- Google OAuth flow:
/api/oauth/google/startinitiates OAuth withgmail.readonlyscope;/api/oauth/google/callbackexchanges code and stores tokens viaupsertOAuthToken. - Token storage: new
user_oauth_tokenstable (Neon). OAuth DB ops extracted tosrc/lib/db-oauth.tsto keepdb.tsunder 300 lines. Refresh logic +statusfield (active/revoked/refresh_failed) insrc/lib/gmail-oauth.ts. - Sync engine:
/api/gmail/sync— fetches Gmail transaction alerts, parses with Gemini Flash (gemini-email-parse.ts), deduplicates viatransactions.dedup_hashGENERATED ALWAYS column, inserts, re-triggers advisory pipeline. - Cron fan-out:
/api/cron/gmail-sync(master, 01:30 UTC daily) fans out to per-userworker. Pattern mirrorsweekly-recapfan-out exactly. - Sync status API:
/api/gmail/sync/statusreturns last sync run counts + errors;logSyncRunextracted tolog-sync-run.ts. - Dashboard Sync tab:
SyncPanel.tsxreplacesUploadPanelas the primary tab. Sub-components (SyncResultCard,SyncRunHistoryList) extracted toSyncPanelParts.tsx. PDF upload fallback toggle preserved insideSyncPanel. - Schema additions:
user_oauth_tokens,gmail_sync_runs,transactions.dedup_hash(GENERATED ALWAYS),statements.ingestion_source, extendedstatement_typeCHECK to includegmail_sync. Applied viaschema-upgrades.ts. vercel.json: added{ "path": "/api/cron/gmail-sync", "schedule": "30 1 * * *" }to crons array.- Deploy: commit
5690212onmain→ productionhttps://money-mirror-rho.vercel.app.
commands/gmail-sync.md: new utility command — fetches Gmail transaction emails via Claude Code's Gmail MCP tools, parses each with Gemini Flash, POSTs to Money Mirror's/api/gmail/sync, prints scanned/inserted/skipped summary.CLAUDE.md: registered/gmail-syncin the Utility Commands list.
- MoneyMirror Vercel reconnect: the existing
money-mirrorVercel project was reconnected toshadowdevcode/ai-product-osafter confirming the project hadcreateDeployments = "enabled"but no active Git link. Production branch is now explicitlymain, rooted atapps/money-mirror. - Main-only MoneyMirror deploys:
apps/money-mirror/vercel.jsonnow blocks non-mainGit deployments for the app while preserving cron configuration. - Proof deployment: commit
133e62d(docs(money-mirror): note main-only production deploys) automatically created production deploymentmoney-mirror-3so34gj1d-vijay-sehgals-projects.vercel.app, which now backsmoney-mirror-rho.vercel.app. - Landing isolation: new
apps/landing/vercel.jsonadds a path-scopedignoreCommandso theai-product-os-493eproject skips builds whenapps/landingdid not change. Runtime verification: config-fix commitf7c18ffproduced a normal landing production build, while later non-landing pushes were canceled in 1-2 seconds instead of running a normal production build.
- Privacy contract enforcement (server):
apps/money-mirror/src/app/api/guided-review/outcome/route.tsnow forcescommitment_text = NULLwheneverdismissed = true, regardless of client payload. Telemetry uses the server-derived value. Regression test added at__tests__/route.test.ts. - Cluster rollup truthfulness: New helper
fetchClusterMerchantAggregates()indashboard-helpers.tsruns a full-scope SQL aggregate bound bymerchant_key = ANY(ALL_CLUSTER_MERCHANT_KEYS)(no LIMIT).frequency-clusters/route.tsnow 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 infrequency-clusters/__tests__/route.test.tsproves cluster totals include merchants absent from the top-N sample. - Scope-keyed SLA timers:
DashboardClient.tsxnow resetsmountTimeRef,dashboardReadyFiredRef, andadvisoryReadyFiredRefwheneverdashboardScopeKeychanges, sodashboard_ready_msandtime_to_first_advisory_msre-fire per scope rather than only on first mount. - Prompt autopsy applied:
agents/backend-engineer-agent.md§5 (server-authoritative consent flags),agents/backend-architect-agent.mdchecklist item 15 (full-scope rollup truthfulness),agents/frontend-engineer-agent.md§6 (scope-keyed SLA timers). - Verification:
npm test159/159 ✅,npm run lint✅,npm run build✅ (apps/money-mirror). - Source:
experiments/results/peer-review-012.md(BLOCKED → fixes for both blockers + medium).
- Linear: Project state Planned; root VIJ-52 → Todo (Planning); document issue-012 Plan Snapshot; child tasks VIJ-53–VIJ-64 from
experiments/plans/manifest-012.json. - Repo:
experiments/linear-sync/issue-012.jsontask map +manifest-012.jsonlinear.tasksidentifiers.
- Artifacts:
experiments/plans/plan-012.md,experiments/plans/manifest-012.json - 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). - Linear: root VIJ-52;
/linear-sync plancheckpoint to sync PRD + child tasks. - Next pipeline step:
/execute-plan(start phase-t0).
- Type: Enhancement
- Title: MoneyMirror — Gen Z clarity loop: emotional UX, frequency-first insights, and performance-to-insight SLAs
- App: apps/money-mirror
- Status: Discovery
- Linear: root VIJ-52, project issue-012 — Gen Z clarity loop
- Source:
.cursor/plans/money_mirror_10x_issue_34a61725.plan.md
- App:
apps/money-mirror - Paywall visibility for local QA:
.env.local.examplesetsNEXT_PUBLIC_PAYWALL_PROMPT_ENABLED=1(copy still opt-in per deploy); local.env.localcan mirror for manual Overview testing (gitignored). - Regression script:
npm run test:issue-011runs 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). - README: Testing table + short manual QA note for P4-E thresholds (points to
bad-pattern-signals.ts).
2026-04-06 — /execute-plan issue-011: P4-D/B/C/H completion (proactive, ingestion trust, chat, hardening)
- App:
apps/money-mirror - Proactive channels (P4-D): added
POST /api/proactive/whatsapp-opt-in(auth required, env-gated provider relay, telemetry-only stub fallback) andPOST /api/proactive/push-subscription. - Ingestion trust (P4-B): Upload panel now shows an explicit Retry upload CTA on parse errors; README adds golden PDF regression runbook.
- Facts-only chat (P4-C): added
POST /api/chatwith 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). - Hardening (P4-H): introduced shared
src/lib/rate-limit.ts; enforced per-user heavy-read limits onGET /api/dashboard,GET /api/transactions, andGET /api/insights/merchants; emitsrate_limit_hit. - Docs/env/tests:
.env.local.examplenow includesWHATSAPP_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).
- App:
apps/money-mirror - Schema:
profiles.plan(free|pro, defaultfree); idempotent DDL inschema.sql+schema-upgrades.ts. - API:
GET /api/dashboardandPOST /api/statement/parseresponses includeplan;normalizeUserPlaninsrc/lib/user-plan.ts. - UI:
PaywallPrompton Overview after the Money Mirror block is visible (IntersectionObserver) whenNEXT_PUBLIC_PAYWALL_PROMPT_ENABLED=1; dismiss stored inlocalStorage. - Telemetry: client
paywall_prompt_seen,upgrade_intent_tappedviaposthog-browser.ts(requiresNEXT_PUBLIC_POSTHOG_KEY). - Docs: README Product Hunt section + analytics table rows;
.env.local.exampledocumentsNEXT_PUBLIC_PAYWALL_PROMPT_ENABLED. - Tests: Vitest includes
user-plan.test.ts. Next (issue-011): P4-F / VIJ-47 permanifest-011.json.
- App:
apps/money-mirror - Advisories:
MICRO_UPI_DRAIN,REPEAT_MERCHANT_NOISE,CC_MIN_DUE_INCOME_STRESSinadvisory-engine.ts(thresholds inbad-pattern-signals.ts); dashboard aggregates extended indashboard-unified.ts/dashboard-legacy.ts(micro-UPI sums, top repeatmerchant_key, CC min-due vs scope). - API:
GET /api/transactions?upi_micro=1filters small UPI debits (≤₹500 with VPA);transactions_filter_appliedincludesupi_microinfilter_typeswhen set. - UI:
AdvisoryFeedCTA →/dashboard?tab=transactionswith scope-preservingmerchant_key/upi_micro;TxnFilterBarbanner for micro-UPI filter. - Telemetry:
bad_pattern_advisory_shown/bad_pattern_advisory_clickedviaposthog-browser.ts(requiresNEXT_PUBLIC_POSTHOG_KEY). Layer A facts:micro_upi_debit_paisa,repeat_merchant_noise_paisa,cc_minimum_due_income_ratio. - Tests: 92 passing. Next (issue-011): P4-G / VIJ-46 per
manifest-011.json.
- App:
apps/money-mirror - Schema:
transactions.upi_handle; tablesuser_merchant_aliases,merchant_label_suggestions(+ indexes); idempotent DDL inschema.sqlandsrc/lib/schema-upgrades.ts. - Parse:
extractUpiHandle+ persist on insert (persist-statement.ts);merchant-normalizeexportsformatMerchantKeyForDisplay. - API:
GET|POST|DELETE /api/merchants/alias,POST /api/merchants/suggest-accept,GET /api/cron/merchant-enrich(weekly cron invercel.json);GET /api/transactionsand merchant rollups join aliases; rollups returndisplay_label+ optional Gemini suggestion fields. - UI:
TxnRowUPI chip + alias-aware label;MerchantRollupsrename modal + “Use AI suggestion”. - Telemetry:
merchant_alias_saved,merchant_suggestion_accepted(server-side). Tests: 87 passing. - Docs:
apps/money-mirror/README.mdupdated (endpoints, analytics, schema). Next (issue-011): P4-E / VIJ-45 permanifest-011.json.
- Artifacts:
experiments/plans/plan-011.md(PRD, UX, architecture, DB, AC per epic P4-A–P4-H);experiments/plans/manifest-011.json(phases, tasks,posthog_events, env vars). - Linear:
/linear-sync plan— project Planned; issue-011 Plan Snapshot; child epics VIJ-44 (P4-A) through VIJ-51 (P4-C), VIJ-50 (P4-H); root VIJ-43 updated. Sync mapexperiments/linear-sync/issue-011.json. - State:
project-state.md— stageexecute-plan, quality gatecreate_plandone; next/execute-planfrom VIJ-44.
- Type: Enhancement
- Title: MoneyMirror Phase 4 — Merchant-native visibility, proactive coaching, and growth readiness
- App: apps/money-mirror
- Status: Discovery
- Linear: Project issue-011 — MoneyMirror Phase 4 — merchant visibility & growth; root VIJ-43
App: apps/money-mirror
What: Refreshed 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 and docs/SCHEMA-DRIFT.md.
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).
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.
App: apps/money-mirror
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.
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).
Fix: scripts/apply-schema-upgrades.ts — wrap in main() (no top-level await) so tsx/esbuild can run npm run db:upgrade.
App: apps/money-mirror
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).
App: apps/money-mirror
What: Layer A facts (src/lib/coaching-facts.ts, Zod + buildLayerAFacts), Gemini 2.5 Flash structured narratives with cited_fact_ids validation (src/lib/gemini-coaching-narrative.ts), attachCoachingLayer (src/lib/coaching-enrich.ts) wired into GET /api/dashboard and GET /api/dashboard/advisories, POST /api/dashboard/coaching-facts-expanded for coaching_facts_expanded telemetry, FactsDrawer + updated AdvisoryFeed, dashboard.signals for food/subscription heuristics, dependency zod. README, docs/COACHING-TONE.md, CODEBASE-CONTEXT.md, manifest-010.json PostHog list updated.
App: apps/money-mirror
What: Implemented the unified scope model from experiments/plans/plan-010.md Phase B: shared src/lib/scope.ts, extended GET /api/dashboard and fetchDashboardData (legacy single statement vs date_from/date_to/statement_ids), statement_ids filter on GET /api/transactions, ScopeBar UI with POST /api/dashboard/scope-changed for PostHog scope_changed, and aligned TransactionsPanel with the same scope. Mirror “perceived” uses profiles.perceived_spend_paisa (single baseline). README and project-state.md updated.
- Type: Enhancement
- Title: MoneyMirror Phase 3 — Unified multi-source dashboard, transaction-native insights, and expert AI coaching
- App: apps/money-mirror
- Status: Discovery
What: Closed remaining documentation gaps after Phase 2 ship and VIJ-24 closure.
Root README.md:
- Added MoneyMirror (issue-009) to the Projects Built table — was missing entirely despite being live at
https://money-mirror-rho.vercel.app. - Added v3 row to System Evolution table covering the MoneyMirror pipeline cycle.
- Rewrote "What it does" to reflect Phase 2: 3-tab dashboard (Overview/Insights/Upload), statement library, month picker, mirror card (perceived vs actual), upload labels, multi-account.
- Added
GET /api/statementsto the API section. - Added Docs section linking to
docs/COACHING-TONE.md. - Updated "Current scope" into explicit Shipped / Not shipped (Sprint 4 backlog) sections.
Linear: VIJ-24 confirmed Done (Neon label columns verified applied). VIJ-25 remains Backlog (Sprint 4 — F3, G2–G3, H3).
What: Aligned project-state.md, experiments/linear-sync/issue-009.json, and Linear with post-merge engineering work.
GitHub: PR #16 merged to main (3cdd83a) — Phase 2 dashboard/statements, parse-route splits, schema.sql label columns.
Neon / VIJ-24: Idempotent ALTER TABLE for statements.nickname, account_purpose, card_network applied; columns verified. VIJ-24 → Done (comment + closeout). VIJ-23 — traceability comment (PR #16, prod URL).
Repo: issue-009.json — pr_link → PR #16, pr_link_superseded → PR #15, last_sync_mode manual-post-merge-vij24-pr16. Not a slash /linear-sync run; Linear updated via MCP.
What: Recorded MoneyMirror Phase 2 (dashboard roadmap) delivery in Linear and refreshed project-state.md.
Linear (under VIJ-11):
- VIJ-23 — Done — Phase 2 shipped in repo (epics A–H baseline).
- VIJ-24 — superseded by 2026-04-05 — Repo sync above (now Done).
- VIJ-25 — Backlog — F3, G2–G3, H3 follow-ups.
Repo: experiments/linear-sync/issue-009.json — tasks map + last_sync_mode updated for manual Phase 2 issue creation (not a full /linear-sync pipeline run).
Update (same day): Full Sprint 1–3 (VIJ-26–28, Done), Sprint 4 / Backlog (VIJ-25), Epics A–H (VIJ-29–36, Done) created in Linear and mirrored in project-state.md § MoneyMirror PM roadmap — Linear map.
What: Pushed feat/linear-workflow-sync (commits 9f483ed, dced451) and opened PR #15 for review: Neon MCP secret removal, .codex/config.toml, CHANGELOG updates.
Docs: project-state.md updated — last_commit dced451, open_pr_link → PR #15, decisions log entry for MCP/Codex hygiene.
Linear: VIJ-11 is Done (API check 2026-04-04). experiments/linear-sync/issue-009.json still reflects last pipeline sync 2026-04-04T08:22:36Z (phase-1-rollout-closeout); no new /linear-sync run for MCP-only commits (PM milestone unchanged). pr_link in the JSON map points to PR #15 as the active review link.
What: OpenAI Codex does not read .mcp.json; it uses TOML under .codex/config.toml. Added a committed-safe project config that wires Neon’s streamable HTTP MCP via bearer_token_env_var = "NEON_API_KEY" (no secrets in Git).
Setup:
- Create or rotate a Neon API key in Neon Console.
- Export the key where Codex runs, e.g.
export NEON_API_KEY="napi_…"in~/.zshrc/~/.bashrc, or configure your terminal/IDE env so Codex inherits it. - In Codex, mark this repo as a trusted project so project-scoped MCP loads (see Codex docs).
- Cursor / Claude Code still use a local gitignored
.mcp.jsonfrom.mcp.json.exampleif you need manual HTTP MCP; you can paste the same key there or rely on the Neon Cursor plugin + MCP UI.
Other MCP servers (Linear, Vercel, etc.): Add via codex mcp add … or extra [mcp_servers.*] tables per Codex MCP docs; do not commit bearer tokens—use bearer_token_env_var or OAuth (codex mcp login) where supported.
What: A Neon API key was committed in project-root .mcp.json. That key must be treated as compromised.
Required (human, Neon Console):
- Revoke the leaked key immediately: Neon Console → Account settings → API keys (or organization keys, depending on where
napi_…keys are managed). - Create a new key for local use only. Never commit it.
Repository changes:
- Removed tracked
.mcp.jsonand added.mcp.jsonto.gitignoreso local MCP config stays out of Git. - Added
.mcp.json.exampleas a template (placeholder only, no secrets).
How to configure Cursor safely:
- Prefer the Neon Postgres Cursor plugin (already enabled under
.cursor/settings.json) and complete auth in Cursor → Settings → MCP. - If you need a manual HTTP MCP entry: copy
.mcp.json.example→.mcp.json, paste your new key, and keep.mcp.jsonlocal (gitignored).
Git history: Old commits may still contain the leaked secret. Revoking the key in Neon closes the practical risk. To remove the blob from history (e.g. public fork), use git filter-repo or similar on a coordinated branch and force-push, understanding collaborator impact.
What: Executed the VIJ-20 production deploy attempt for apps/money-mirror, fixed one Vercel runtime incompatibility in code, created the Vercel project, and captured the remaining production blocker.
Vercel setup completed:
- Created project
money-mirrorunder scopevijay-sehgals-projects - Linked local app directory to the Vercel project
- Synced production env vars from
apps/money-mirror/.env.local - Corrected
NEXT_PUBLIC_APP_URLto the real assigned alias:https://money-mirror-rho.vercel.app
Code fix applied:
- Replaced
apps/money-mirror/middleware.tswithapps/money-mirror/proxy.ts - Why: the initial production deploy failed because Vercel rejected
@neondatabase/auth/next/serverinside the Edgemiddlewareruntime - Result: Next 16 auth gating now runs through the Node
proxyfile convention instead of Edge middleware
Validation after the fix:
npm testPASS (45 tests)npx next build --webpackPASSnpx tsc --noEmitPASS after regenerating.next/types- Vercel production build PASS
Remaining blocker:
- Public deployment URLs are protected by Vercel Authentication (
401 Authentication Required) - Even authenticated
vercel curlrequests still returnNOT_FOUNDfor/,/login,/dashboard, and/api/cron/weekly-recap - This indicates the release is still blocked by Vercel project/public routing configuration, not by the app build itself
Why: The original remaining task for Phase 1 was deploy + production verification. That surfaced two separate production issues: one app/runtime issue (fixed) and one Vercel serving/protection issue (still open).
What: Completed full Phase 1 rollout validation against live external services (Neon Auth, Gemini, Neon DB).
Neon schema migration:
- Applied 7
ALTER TABLEstatements to live DBsteep-meadow-97750093 profilesgainedmonthly_income_paisa BIGINTstatementsgainedinstitution_name,statement_type(with CHECK constraint),due_date,payment_due_paisa,minimum_due_paisa,credit_limit_paisa
Smoke test results (all via Playwright against http://localhost:3000):
- Dev server boot: Next.js 16 Turbopack ready in ~440ms ✅
- Cron gate: 401 unauthenticated, 200 with
x-cron-secret✅ - OTP login: email submitted, OTP entered, session established ✅
- Bank account upload (Kotak Feb 2026): 24 transactions, ₹31,926 debits — DB confirmed
status=processed✅ - Credit card upload (HDFC Feb–Mar 2026): 18 transactions, ₹16,245 spent, credit card fields rendered — DB confirmed ✅
Bug fix — Gemini 2.5 Flash timeout:
gemini-2.5-flashhas thinking enabled by default in 2026. On 10K-char PDF text, this exceeded the 25s route timeout.- Fixed in
apps/money-mirror/src/app/api/statement/parse/route.ts: addedconfig: { thinkingConfig: { thinkingBudget: 0 } }to thegenerateContentcall. - Response time reduced from >25s (timeout) to ~8s.
Linear cleanup (VIJ-11 children):
- VIJ-12 → Duplicate, VIJ-14 → Cancelled, VIJ-15 → Duplicate
- VIJ-13 restructured as
[MoneyMirror] Phase 1 Rollout Validation, In Progress - 6 child sub-issues created: VIJ-16 (schema, Done), VIJ-17 (OTP, Done), VIJ-18 (bank upload, Done), VIJ-19 (CC upload, Done), VIJ-20 (Vercel deploy, Todo), VIJ-21 (cron gate, Done)
Why: DB schema drift blocked all live smoke. Gemini thinking mode silently broke PDF parsing on the happy path.
What: Started the real rollout-validation pass for apps/money-mirror and synced the findings into Linear and repo state.
- Created dedicated Linear follow-up issue
VIJ-13for the live smoke and rollout checklist. - Marked duplicate follow-up
VIJ-14as Duplicate to keep one canonical validation thread. - Verified local runtime behavior:
npm run devboots successfully outside the sandbox- unauthenticated
GET /api/cron/weekly-recapreturns401 - authenticated
GET /api/cron/weekly-recapwithx-cron-secretreturns200 {"ok":true,"total":0,"succeeded":0,"failed":0}
- Verified the target Neon DB is still behind the repo schema:
profilesis missingmonthly_income_paisastatementsstill usesbank_namestatementsis missinginstitution_name,statement_type, and the new credit-card due metadata columns
Why: The repo now contains Phase 1 expansion code for explicit bank_account and credit_card flows, but the live DB has not yet been migrated. Until apps/money-mirror/schema.sql is applied, the real OTP/onboarding/upload smoke cannot validate the current app behavior.
What: Completed the /learning command for MoneyMirror (issue-009). Extracted 7 durable engineering rules from the postmortem and applied Prompt Autopsy changes to 5 agent/command files.
Engineering rules written to knowledge/engineering-lessons.md:
- Dashboard/report pages linked from email CTAs must specify a first-load rehydration path (read path) — the post-mutation result path is insufficient.
- Parent + child write sequences must declare an explicit atomicity strategy — partial success (
parent = processed, children = missing) is never a terminal state. - Fan-out worker HTTP contracts must use non-2xx status for failure — master must not inspect JSON body for success/failure accounting.
- Auth route fixes must update all callers in the same change — a route auth fix without caller verification is an incomplete fix.
.env.local.examplemust be generated by greppingprocess.env.*in source — not from memory. Key name divergence is a deploy blocker.- File size limits (route < 200 lines, page < 250 lines) must be applied during code generation — not discovered at pre-commit hook rejection.
- Third-party library API must be verified against installed version before marking integration complete — training knowledge is not sufficient.
Agent/command files updated per Prompt Autopsy:
agents/backend-architect-agent.md: 3 new Mandatory Pre-Approval Checklist items (10: rehydration path, 11: write atomicity, 12: fan-out HTTP contract)agents/backend-engineer-agent.md: 2 hard rules (auth caller cross-verification, file size budget at generation time)agents/code-review-agent.md: 2 new checks (authenticated route caller verification = CRITICAL, parent/child write sequence check = CRITICAL)agents/qa-agent.md: env var key name cross-check added as standalone QA dimension with grep-based verificationcommands/execute-plan.md: env var grep step in §8 completion checklist; read/write path checkpoint + third-party library verification in §5; file size budget rule as §5b
Also written: apps/money-mirror/CODEBASE-CONTEXT.md — full AI context file for future agent sessions.
Why: Recurring failure patterns from issue-009 (2nd consecutive parent/child write atomicity gap, 3rd consecutive env var naming issue, file size violations deferred 3 stages). Upstream enforcement needed to break these cycles.
2026-04-03 — Shift-Left Infra Validation: Gate 0 + Empty ENV Detection + Execute-Plan Provisioning Checklist
What: Three coordinated changes to eliminate the "tests pass, app broken locally" failure pattern discovered during issue-009 analysis.
Change 1 — /deploy-check Gate 0 (manual smoke test):
Added a pre-flight checklist (Gate 0) that the PM runs manually before triggering /deploy-check. Six checkboxes: dev server boots, OTP login works, onboarding writes to DB, core feature works end-to-end, no 500 errors, no empty env vars. If any fail, fix before running the command. Updated the Output Format to include "Local Smoke Test (Gate 0 — PM confirmed)" as the first line.
Change 2 — /deploy-check ENV gate upgrade (empty value detection):
The existing ENV gate only checked that variable names appeared in .env.local.example. Upgraded it to read .env.local directly and classify each variable as FILLED / EMPTY / MISSING. Empty values (VAR= or VAR="") are now a blocking violation — previously they passed as "present". Variables explicitly marked # Optional are exempt.
Change 3 — /execute-plan + backend-engineer-agent.md infra provisioning:
Added a 6-item infrastructure provisioning checklist to the execute-plan completion criteria. DB project created, schema applied and verified, auth provider provisioned (e.g., Neon Auth URL obtained), all non-optional env vars filled, Sentry project created and configured, npm run dev boots clean. These are now hard deliverables — not README suggestions. Also moved Sentry setup from deploy-check into execute-plan's Backend Implementation section: npm install @sentry/nextjs, wizard run, all 4 Sentry vars filled. The backend-engineer-agent.md # Rules section now lists all 6 as explicit hard constraints.
Why: Issue-009 analysis revealed that NEON_AUTH_BASE_URL was empty in .env.local — OTP login would have failed immediately on local test. RESEND_API_KEY was also empty, meaning recap emails silently fail (returning 200 but not sending). The current ENV gate in deploy-check passed both because it only checked variable name presence in .env.local.example, not actual values. The root cause: infra setup (Neon Auth provisioning, Sentry project creation) has no enforcement point in the pipeline — it lives only as prose in README.md. Deploy-check discovered these gaps too late (after PR creation). The fix shifts this validation to execute-plan, where the engineer is still in implementation mode.
Files: commands/deploy-check.md (Gate 0 added, §2 ENV gate upgraded, output format updated), commands/execute-plan.md (§2 Sentry setup added, §8 infra provisioning checklist added), agents/backend-engineer-agent.md (# Rules section updated with 6-item infra checklist)
What: Upgraded /deploy-check so that database schema application and ENV completeness are enforced as blocking gates during the command, not left as unchecked items in the PR body for a reviewer to discover.
- New §3a (Database Schema Verification): Agent reads
schema.sql, extracts allCREATE TABLEtable names, then either queriesinformation_schema.tablesvia MCP (Supabase/Neon) to verify each table exists, or — if MCP is unavailable — prints a blocking prompt listing every required table and instructs the user to apply the schema before continuing. Deployment is blocked if any table is missing or the user hasn't confirmed. - Updated §2 (ENV Completeness Check): Agent greps
apps/<project>/src/for allprocess.env.*references and diffs against.env.local.example. Any var in code but missing from the example file is a BLOCKING violation that stops the command. - Updated §8 PR body: Removed
[ ] Apply schema.sqland[ ] Set env varsas reviewer TODOs — replaced with pre-checked[x] Schema verifiedand[x] ENV verifiedlines, because these are now confirmed before PR creation.
Why: Across multiple pipeline cycles (issues 002–006, 009), the schema and ENV steps were only surfaced as PR checklist items that reviewers were expected to catch. This caused silent deploy failures: the PR was merged, the app was pushed, and only then did the missing tables or missing env vars surface. The gate must fire before the PR is created.
Anti-pattern fixed: "Schema applied post-PR = silent deploy failure" (engineering-lessons.md, issue-002 entry).
Files: commands/deploy-check.md (§2 ENV check added, §3a schema verification added, §8 PR body updated), CHANGELOG.md
What: Added two hard rules to the system that were previously missing: (1) PM feedback during any pipeline stage must be captured immediately into the relevant agent/command file and CHANGELOG — not deferred to /learning. (2) Linear sync checkpoints are now mandatory, not "recommended" — if a sync is skipped, the next command must run it before proceeding.
Why: PM feedback during issue-009's pipeline was not being captured into the agent files in real time, creating risk that corrections would be lost if the cycle was abandoned or compacted. Linear syncs were being skipped because the language in CLAUDE.md and command-protocol.md said "recommended" rather than enforcing them.
Files: CLAUDE.md (Real-Time Feedback Capture section added, Linear checkpoints changed from Recommended to Mandatory), command-protocol.md (Real-Time Feedback Capture Protocol section added, CHANGELOG Discipline section added, Linear checkpoints made mandatory)
What: Strengthened /review command and code-review-agent.md based on a benchmark against Zevi Arnovitz's (Meta PM, Lenny's Podcast) code review command. Added what was genuinely better; kept our competitive advantages.
Added to both files:
- Formal severity ladder: CRITICAL / HIGH / MEDIUM / LOW with project-specific definitions (PostHog dual-emission explicitly labeled CRITICAL)
- Structured output format:
Looks Cleanpass-list +Issues Foundwithfile:lineformat +Summaryblock with issue counts and recommendation - Explicit production readiness checks: no
console.log, no TODOs/FIXMEs, no hardcoded secrets, no@ts-ignore - React/Hooks review step (scoped strictly to
"use client"files): effect cleanup, dependency arrays, infinite loop patterns - Client-side performance sub-checks under Step 5 (
useMemo,useCallback, unnecessary re-renders) — also scoped to Client Components only
Preserved (our advantages Zevi doesn't have):
- PostHog dual-emission check as a named CRITICAL block with exact grep instructions
- Architecture check diffs against the actual plan doc (not generic "follows patterns")
- Knowledge file loading (
engineering-lessons.mdkeeps postmortem rules active) - Pipeline gate integration and quality gate enforcement
What we did NOT copy: emoji formatting, generic architecture check, project-agnostic output.
Files: commands/review.md (updated), agents/code-review-agent.md (updated)
What: Full Linear integration layer added as a PM-facing workflow mirror. The repo remains the source of truth; Linear reflects state for stakeholder visibility.
- Created
commands/linear-bind.md— binds active repo issue to a Linear team and project; createsexperiments/linear-sync/issue-<NNN>.jsonsync map; writeslinear_enabled: trueand all binding fields toproject-state.md - Created
commands/linear-sync.md— mirrors repo artifacts into Linear in 4 modes:issue(brief),plan(document snapshot + child issues from manifest),status(stage labels + blockers),release(PR/deployment links) - Created
commands/linear-brief.md— read-only summary of current Linear view; compares repo stage against Linear status; identifies mismatches - Created
commands/linear-close.md— finalizes Linear project after/learning; sets project tocompleted, root issue toDone, creates closeout snapshot document - Created
agents/linear-agent.md— Linear Agent role definition (product operations specialist); idempotent sync behavior; repo-as-source-of-truth constraint - Created
knowledge/linear-operations.md— shared runtime rules: sync map schema, naming conventions, label taxonomy (AI Product OSparent + 7 children), status mapping, failure policy - Registered all 4 commands as
.claude/commands/stubs - Added Linear PM Layer section to
CLAUDE.mdwith recommended sync checkpoints - Updated
commands/create-issue.mdto auto-bind Linear at the end of every new issue creation (no manual/linear-bindstep required going forward) - Retroactively synced issues 002–006 and 008 to Linear as
Completed
Why: Pipeline had no PM-facing visibility layer. Issues were built, shipped, and archived with zero Linear record. Linear now mirrors all 6 completed projects with closeout snapshots, enabling portfolio visibility and cycle velocity tracking.
Pipeline isolation: Linear commands are utility-only. They do not alter stage progression, do not interact with experiments/ artifact content, and cannot block or unblock pipeline stages.
Files: commands/linear-bind.md (new), commands/linear-sync.md (new), commands/linear-brief.md (new), commands/linear-close.md (new), agents/linear-agent.md (new), knowledge/linear-operations.md (new), .claude/commands/linear-*.md (4 new stubs), CLAUDE.md (updated), commands/create-issue.md (updated), experiments/linear-sync/issue-{002,003,004,005,006,008}.json (new)
What: New standalone utility command /assign-reviewers that assesses PR risk from the actual code diff and routes accordingly.
- Created
.claude/commands/assign-reviewers.md— skill stub that registers the command in Claude Code - Created
commands/assign-reviewers.md— full protocol: adversarial PR content handling, 5-tier risk model (Very Low → High), reviewer selection viagit log/git blame, approval/unapproval logic, PR comment, Slack notification - Created
.github/workflows/pr-auto-review.yml— GitHub Action that triggers onpull_request: [opened, synchronize], skips bot PRs, and calls/assign-reviewers <PR-URL>via Claude Code CLI
Why: Manual reviewer assignment is inconsistent and slow. Risk-based routing ensures high-risk PRs always get eyes, low-risk PRs get approved without friction, and the decision is derived from actual diffs — not PR description claims (adversarial input model).
How triggers work: GitHub Action fires on PR open and PR push (synchronize). It calls Claude Code CLI with --dangerously-skip-permissions in a non-interactive context, passing the PR URL. Slack notifications are opt-in via SLACK_WEBHOOK_URL secret.
Required GitHub secrets: ANTHROPIC_API_KEY (required), SLACK_WEBHOOK_URL (optional — skip Slack if absent).
Pipeline isolation: This command has no pipeline role. It does not read or write project-state.md, does not interact with experiments/ or knowledge/, and cannot block or unblock any pipeline stage.
Files: commands/assign-reviewers.md (new), .claude/commands/assign-reviewers.md (new), .github/workflows/pr-auto-review.yml (new)
What: Global ~/.claude/ configuration for cost and token optimization across all projects.
- Created
~/.claude/CLAUDE.md— lean 16-line global instructions (context management, subagent tiers, efficiency habits) - Updated
~/.claude/settings.json— default modelopus→sonnet(5x cheaper for 80% of tasks),MAX_THINKING_TOKENS=10000(70% thinking cost reduction from 32K default),CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=50(earlier compaction prevents context bloat)
Why: Project-level optimizations (knowledge subsetting, hooks) save 40-60% per command but only apply to this repo. Global settings compound those savings across all projects. Boris Cherny recommends lean CLAUDE.md for prompt cache efficiency (cached reads = 10% cost). Community consensus: Sonnet for 80%+ tasks, Opus only for adversarial/complex work.
Sources: Boris Cherny (Anthropic) — prompt caching, lean CLAUDE.md; community power users — model defaults, thinking token caps, auto-compaction thresholds
Files: ~/.claude/CLAUDE.md (new), ~/.claude/settings.json (modified)
What: Major system-wide upgrade inspired by Boris Cherny (Anthropic), Zevi Arnovitz (Meta PM), Harness Engineering, and ChatPRD/Lenny podcast practitioners.
- Added
## Required Knowledgesections to all 15 command files — each command now loads only relevant knowledge files instead of all 9 (2,820 lines) - Updated loading directives in CLAUDE.md, command-protocol.md, and .claude/rules/commands.md
- Added
/compactguidance section to CLAUDE.md - Added model routing reminders (Opus) to peer-review and postmortem slash commands
- Updated all 12 .claude/commands/ files to reference command-level subsetting
- Created
scripts/lib/check-gate-before-write.js— Claude Code PreToolUse hook blocks writes to apps/ when pipeline status is "blocked" - Created
scripts/lib/check-patterns.js— pre-commit grep checker for top 3 postmortem anti-patterns (fire-and-forget, missing .limit(), naked JSON.parse) - Added hooks configuration to
.claude/settings.json(PreToolUse gate check, PostToolUse function size check) - Updated
.husky/pre-commitwith check-patterns.js and generate-claude-sections.js
- Added
## 0 Task Breakdownsection to execute-plan.md — atomic task decomposition (name, files, size S/M/L, dependencies) presented to PM before any code is written - Added Step 8.5 (context management advisory) to command-protocol.md
- Added Subagent Cost Tiers + Multi-Model Guidance to ai-model-guide.md (Haiku for exploration, Sonnet for implementation, Opus for adversarial review)
- Created
scripts/lib/generate-claude-sections.js— auto-regenerates CLAUDE.md anti-patterns from engineering-lessons.md - Added AUTO markers to CLAUDE.md anti-patterns section
- Created
pm-assets/directory (gitignored) for personal PM career materials - Created
pm-assets/README.mdwith template structure (context.md, deck/, prd/, email-drafts/, interview-prep/) - Added optional PM Portfolio Output section to deploy-check.md
- Hardened
.gitignore: added pm-assets/, .claude/plans/, .claude/todos/, _.pptx, _.docx, **/deck/, playwright artifacts
- Created
knowledge/readiness-framework.md— 5-pillar self-assessment (Pipeline Compliance, Knowledge Currency, Enforcement Coverage, Token Efficiency, Cycle Velocity) with 5 maturity levels (Bare → Autonomous)
Why: System was scoring 8.2/10 — excellent pipeline and learning loop but token-inefficient (all knowledge loaded every command), enforcement was prose-only (gates could be skipped), and no atomic task decomposition. These changes target 9.0+ by making enforcement mechanical, context lean, and execution granular.
Sources: Boris Cherny (lean context, /compact, lint rules from review), Zevi Arnovitz (strict plan execution, model specialization), Harness Engineering (three-layer enforcement, auto-markers, readiness pillars), ChatPRD/Lenny (atomic tasks, subagent cost tiers)
Files: 15 commands/.md, 12 .claude/commands/.md, CLAUDE.md, command-protocol.md, .claude/rules/commands.md, .claude/settings.json, .husky/pre-commit, .gitignore, knowledge/ai-model-guide.md, knowledge/readiness-framework.md, scripts/lib/check-gate-before-write.js, scripts/lib/check-patterns.js, scripts/lib/generate-claude-sections.js, pm-assets/README.md, commands/deploy-check.md
What: Final archival of the Nykaa Hyper-Personalization project.
- Implemented robust Backend Product API (
GET /api/catalog/product/[id]) to replace client-side mocks. - Refactored Product Details Page (PDP) to use server-side fetch for full "Backend Agent" compliance.
- Updated
project-state.mdand rootREADME.mdto reflect the completed and archived status of the MVP. - Reset the pipeline to
idlefor the next project cycle. Why: Continuous improvement of the AI Product OS. Fixes the "missing backend" gap in issue-008 and ensures the repository is in a clean, professional state for portfolio review. Files:apps/nykaa-personalisation/src/app/api/catalog/product/[id]/route.ts,apps/nykaa-personalisation/src/app/product/[id]/PDPContent.tsx,project-state.md,README.md
System-level changes to the AI Product OS framework. Pipeline issue work (product ideas, plans, QA results) is tracked in experiments/ and project-state.md.
What: /learning cycle complete. 4 engineering rules, 1 product rule extracted. 3 agent files hardened. CODEBASE-CONTEXT.md generated.
Why: Closes the pipeline loop for issue-008. Key new system guardrails: fire-and-forget telemetry in hot paths, A/B salt server-only enforcement, metric-to-UI flow mapping requirement, frontend defensive programming standards.
Files: knowledge/engineering-lessons.md, knowledge/product-lessons.md, knowledge/prompt-library.md, agents/backend-architect-agent.md, agents/backend-engineer-agent.md, agents/frontend-engineer-agent.md, apps/nykaa-personalisation/CODEBASE-CONTEXT.md, project-state.md
What: Static Next.js landing page at apps/landing/ showcasing the pipeline, agents, and shipped products for the ICP.
Why: Vercel deployments were failing (no buildable app at root). Needed a public-facing page explaining what the OS does. Vercel root directory now set to apps/landing.
Files: apps/landing/ (15 files), GitHub homepage URL updated to ai-product-os-493e.vercel.app
What: Root-level husky pre-commit/pre-push hooks, lint-staged, prettier. Enforcement scripts for secrets, file sizes, env files, function sizes, doc validation.
Why: Code was being committed without formatting or safety checks. Pre-commit hooks catch secrets and enforce consistency before code hits the repo.
Files: package.json, .husky/, .lintstagedrc.json, .prettierrc, scripts/lib/
What: Refactored CLAUDE.md from a monolithic 300-line file into contextual rules in .claude/rules/ that auto-load by file pattern.
Why: CLAUDE.md was too large for agents to process efficiently. Rules now load only when relevant (e.g., tech-stack rules load only when editing apps/**/*.ts).
Files: .claude/rules/ (6 rule files), CLAUDE.md (slimmed down)
What: Vitest configs for all apps with shared base config and mock factories for Supabase, Neon, PostHog, Gemini.
Why: No automated tests existed. New API routes and lib functions now require colocated .test.ts files.
Files: apps/*/vitest.config.ts, libs/shared/vitest.config.ts, libs/shared/test-utils.ts
What: AGENTS.md as central agent index. QA agent runs npm test before manual QA. Command protocol recommends next pipeline step.
Why: Agent discovery was hard. QA was fully manual. Users had to remember which command comes next.
Files: AGENTS.md, agents/qa-agent.md, command-protocol.md, system-orchestrator.md
What: CHANGELOG.md format standardized. README.md gets "System Evolution" section showing OS maturity arc (v0 → v1 → v2).
Why: System-level improvements had no visible trail. The PM meta-decisions — iterating on the process itself — were invisible to anyone reviewing the repo.
Files: CHANGELOG.md, README.md
commands/eval.md— /eval assertion-based grading commandlibs/shared/posthog.ts— shared PostHog templatelibs/shared/db.ts— shared Supabase templatelibs/shared/error-handler.ts— shared error handling template
commands/execute-plan.md— TDD mandate (§9), telemetry verification (§10), parallel worktree guidance (§11)commands/create-plan.md— JSON manifest requirement (§6)commands/deploy-check.md— Sentry verification (§7), automated PR creation (§8)agents/backend-architect-agent.md— Security pre-approval gate (RLS, auth, rate limiting, env vars)knowledge/coding-standards.md— Sentry integration section, shared libs referenceknowledge/ai-model-guide.md— Specific model IDs per pipeline stageCLAUDE.md— /eval added to utility commands, libs/shared added to repo structure
Gaps identified from claude-caliper evaluation and Zevi Arnovitz meta-PM workflow alignment.
- P0: TDD mandate, JSON manifest, auto-PR
- P1: Security pre-approval gate, Sentry verification, multi-model routing
- P2: /eval command, shared libs, parallel worktrees