recentExpenses and chart were JSON-stringified with raw amountCents/
expensesCents/paychecksCents values, causing the model to read e.g.
10000 as $10,000 instead of $100. Both arrays are now mapped through
centsToDisplay() before inclusion in the prompt, matching the pattern
already used for totals and category breakdown.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add @custom-variant dark in globals.css for class-based dark mode
- Add ThemeToggle component with localStorage persistence and system preference fallback
- Inject blocking inline script in layout to prevent flash on load
- Apply dark: variants across all components (layout, site-nav, home-dashboard, expense-workspace, paycheck-workspace, recurring-expense-manager) and page headers
- Create openspec/changes/theming-dark-mode with proposal, design, and tasks artifacts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add RecurringExpense model to Prisma schema with migration
- Add lib/recurring-expenses.ts: CRUD + virtual projection per month
- Add /recurring-expenses API routes (GET, POST, PATCH, DELETE)
- Merge projected recurring expenses into dashboard totals and expense list
- Add RecurringExpenseManager component to /add-expense page
- Show amber "Recurring" badge on projected items; hide edit/delete for them
- Highlight active nav tab using usePathname() with hover state
- Fix Turbopack/Prisma stub issue by adding serverExternalPackages to next.config.ts
- Clear stale Turbopack stub in Dockerfile before each build
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add MerchantCorrection model: upsert by merchantName, Category enum
- Check corrections DB first in suggestCategoryForMerchant (source: "learned",
no confirmation required); falls through to rules then Ollama if no match
- Inject recent corrections as few-shot examples in the Ollama prompt so the
model improves even for merchants not yet explicitly corrected
- Add POST /categories/correct route to persist corrections
- Detect category override on form save (suggestedCategory !== chosen category)
and silently fire a correction — no extra UX required
- Fix test isolation: beforeEach re-applies vi.fn() defaults after restoreAllMocks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Wrap getDashboardSnapshot in try/catch; return JSON 500 instead of crashing
- Add prisma/seed.ts with realistic Feb + Mar 2026 data: biweekly $2,850 pay
schedule, $2,430 rent, expenses across all 8 categories
- Update Dockerfile and backup route for host Ollama runtime
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add updateExpense() to lib/expenses.ts
- Add PATCH /expenses/:id route with validation and P2025 not-found handling
- Edit button on each expense card pre-fills form; cancel restores add mode
- Submit dynamically PATCHes or POSTs depending on edit state
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Strengthen prompt with required observations (savings rate, spend-to-income,
top category, anomaly), dollar-formatted amounts, and pay schedule context
- Preserve recommendation array structure; store as JSON array string in DB
- Render recommendations as numbered cards with icons in home dashboard
- Add spend-vs-income progress bar and category flow mini bar chart
- Fix test assertions for new JSON array recommendation format
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>