CK 1.60+ validates generated a2ui ops against a component catalog before
painting, and our agent's ops fail validation — surfaces hang at
"Building interface" forever (MAF also drops RunAgentInput.context, so
the catalog/guidelines never even reach the model). Disable a2ui end to
end until the pipeline matures:
- server.ts: a2ui { enabled: false }; RENDER_TOOLS no longer includes
render_a2ui
- App.tsx: remove the a2ui provider prop
- agent.py: prompt now asks for clean markdown tables for lists and
structured data; render_spending_summary card guidance unchanged
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
by-category and daily-spending accept start_date/end_date (range wins
over cycle); Analytics gets a two-month calendar range picker beside
the cycle selector. Fixes a latent float/Decimal TypeError that 500'd
spending_by_category whenever it had data — the category donut had
never rendered. Regression + range tests added.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Categories fold into 'Otros' past 5 slots (fixed hue order, never
cycled); daily spending excludes future-dated Tasa Cero cuotas; rate
chart gets a legend; PageHeader with the cycle selector as action.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Próximas cuotas: headline is the upcoming billing batch (sum of every
active plan's next cuota, using the rounding-absorbing last cuota
when it's the pending one) — the in-cycle formula was ~always zero
since cuotas bill on the 19th, day one of the next cycle.
- useAgent must name the 'wealthysmart' agent (default crashed the page).
- /transactions/recent excludes future-dated Tasa Cero cuotas.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The anchor stays visible in listings but only its cuotas count toward
actuals — future cuotas intentionally show up in future months as
committed spend.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
BAC zero-interest financing: an anchor purchase splits into N monthly
cuota transactions (truncate-to-0.10 rule, last cuota absorbs rounding;
one cuota per 18th-cut billing cycle on the 19th). Pinned against real
Financiamientos data.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Every dict-returning route the SPA consumes now declares a response
model (auth, bulk, sync-status, notifications). api-types.gen.ts is
generated from app.openapi() via openapi-typescript, and the 22 hand-
written read interfaces in api.ts are now aliases onto the generated
schemas — backend schema drift becomes a typecheck failure, and CI
regenerates the file and fails the deploy if it's stale. The aliasing
immediately caught two real drifts: RecurringItemType was missing
SAVINGS, and raw_charges (untyped JSON column) is now explicitly
shaped at the boundary.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Paste-import now returns each skipped duplicate WITH the matched
existing row (id, merchant, date, amount, source) instead of a bare
count, and accepts allow_duplicates to force-import false positives —
the practical fix for same-day identical purchases hashing together
(UX-20; supersedes the BE-08 hash change, which would have orphaned
every existing hash). POST /transactions/bulk applies
delete/set_category/set_deferred to up to 500 ids atomically. 82 tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
GET /api/v1/transactions/export streams a CSV (optional source/type/
date filters), cookie-authenticated so window.open downloads work.
Buttons on the Budget transactions tab (all) and Salarios (SALARY
only). Quoting and category resolution covered by tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The n8n flows fail invisibly from the app's perspective (review UX-17,
the top trust gap). /api/v1/sync-status derives last-received + cadence
thresholds for BAC, salary, municipal, pensions and exchange rate from
existing data — no n8n integration needed. New Sincronización page
shows per-source freshness and a hint to check n8n; the sidebar item
gets an amber dot when any source is stale. Verified live against the
dev DB (correctly flags its stale BAC data at 43 days).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
analytics.py filters by TransactionType.COMPRA instead of the string
literal; budget.py uses keyword HTTPException args; the agent
middleware parses cookies with stdlib SimpleCookie instead of a regex
(ARCH-10); auth.py's lazy imports are hoisted — there was never an
actual auth/db cycle (ARCH-20); transactions.py hoists or_/and_.
Verified: dev container boots, agent auth paths return 401 for
missing/garbage cookies.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Exchange-rate fetchers log every source failure instead of silently
passing (BE-18); the rate tool exposes fetched_at so staleness is
visible (BE-20). Agent: unbound-session failures raise a clear
RuntimeError (BE-12); net worth converts via get_crc_multipliers with
last-known fallbacks instead of a hardcoded 600 CRC / 1.08 EUR guess,
and reports accounts it cannot convert rather than inventing numbers
(BE-24); budget tool enforces MIN/MAX_YEAR (BE-16); municipal receipts
gain offset paging (BE-17); category analytics prefetches names; the
module docstring pins the read-only tool policy (SEC-09). Note: the
refresh loop never swallowed CancelledError (BaseException since 3.8) —
ARCH-08 was a false positive.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
read_pdf_upload() bounds reads at 10 MB and requires the %PDF magic
before anything reaches pdftotext (SEC-06); rejections land in the
per-file errors list with the filename. Parsing now runs via
asyncio.to_thread so a slow PDF no longer blocks the event loop for up
to 30s (BE-14). Review notes: temp files were already context-managed
(BE-09 overstated) and the pension batch already commits atomically
(BE-10 overstated). 69 tests green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- FK rules via migration 7884505b16b3 (verified on dev with a rolled-
back probe): transactions/recurring items SET NULL on category delete
(this also fixes the 500 that DELETE /categories raised for in-use
categories), water readings CASCADE with their receipt. Models declare
ondelete to stay in sync. (ARCH-09, BE-11)
- compute_actuals_by_source: 3 grouped queries replace 10 single-
aggregate round-trips; behavior pinned by the existing cycle suite.
(BE-21)
- compute_cc_by_category prefetches category names (ARCH-15); agent
pension latest-per-fund resolved in SQL (BE-04); municipal water
consumption batched into one grouped query (BE-05).
- Deterministic pagination: date DESC, id DESC on all transaction
listings. (ARCH-12)
- New agent-tool tests (session binding, JSON-safe output, batched
queries): 64 tests green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Models use Money = Annotated[Decimal, PlainSerializer(float)] so storage
is exact NUMERIC(15,2) (rates 15,6) while JSON keeps emitting plain
numbers — the frontend contract is unchanged, pinned by
test_models_serialization.py. Service layers coerce to float at their
boundaries (projection math, rate multipliers, agent tool output, the
savings constants become Decimal) so no naive Decimal/float mixing can
raise. Migration 7f567c8bafdb applied to dev: per-row values and the
table sum byte-identical before/after; endpoints verified live on the
new schema. 59/59 tests green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The baseline (c3da001a0eb3) was autogenerated against an empty Postgres
and schema-diffed against the live dev schema until column-identical
(two model fixes fell out: deferred_to_next_cycle now declares its
server_default, usersettings.data is JSONB on Postgres to match the
live type). Startup replaces init_db/run_migrations with
run_alembic_upgrade(): pre-Alembic databases that already match the
baseline are adopted via stamp; fresh databases build from the
migration. The section is serialized with a pg advisory lock because
prod uvicorn runs 2 workers, each executing the lifespan. Verified:
adoption + idempotent re-run on the dev DB, fresh 13-table build on an
empty DB, full container boot, 55/55 tests. (ARCH-02, BE-19)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Covers: billing-cycle characterization (the BE-06 convention, boundary
instants, year wraps, deferred transactions), projection engine
(recurring schedules, no-double-count, cumulative balances, overrides,
fresh start), auth (JWT roundtrip/expiry, API tokens, constant-time
creds, rate-limit window), fail-fast settings, and the BAC paste
parser. SQLite in-memory fixtures, exchange rates pinned — no network.
Also doubles as the float-behavior baseline for the Phase 2 Decimal
migration (plan 1.5). config.py moves to SettingsConfigDict to clear
the pydantic deprecation warning from test output.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All 29 call sites now use app.timeutil.utcnow(), which returns naive
UTC via the non-deprecated API. Semantics are unchanged on purpose:
every datetime column is TIMESTAMP WITHOUT TIME ZONE, so going aware
here would poison naive/aware comparisons. The TIMESTAMPTZ migration
(Phase 2) now has a single place to change. (BE-01)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review finding BE-06 (suspected off-by-one in get_cycle_range) is a
FALSE POSITIVE: budget month M composes get_previous_cycle with
get_cycle_range to cover [(M-1)/18, M/18), matching both the code
comment and the frontend convention. Verified against prod data: May
2026 cycle-window SQL sum matches the API (95 txs exactly, refunds to
the colón, 0.13%% delta from one EUR tx). The docstring now spells out
the labeling convention, and month is validated 1-12 (BE-07).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both login endpoints now share verify_admin_credentials (hmac
compare_digest, SEC-02) and a 5/min/IP sliding-window rate limit that
returns 429 with Retry-After (SEC-03). CORS is pinned to the prod
domain and localhost dev origins instead of '*' (SEC-04). The session
cookie's Secure flag is driven by COOKIE_SECURE instead of hardcoded
false (SEC-08).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SECRET_KEY, ADMIN_USERNAME, and ADMIN_PASSWORD no longer have working
defaults: the backend refuses to boot if they are unset, default, or
weak (SEC-01). Token expiry drops from 30 to 7 days (SEC-05). New
COOKIE_SECURE (default true) and CORS_ORIGINS settings. Dev compose now
sources credentials from .env with required-var errors instead of
hardcoding them (ARCH-14); .env.example documents the template.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SQLModel enum columns need col() to expose SQLAlchemy operators like
notin_. Without it the agent tool raised at query build time and the
chat card flashed away.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 'last N transactions' answer was including salary deposits, which the
user reads as expense activity. Filter income types out at the query level.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Backend now exposes /api/auth/login + /api/auth/logout setting an
httpOnly ws_token cookie, and get_current_user accepts either the
cookie (SPA) or a Bearer token (n8n/CLI). AuthContext probes the
cookie via /api/v1/auth/me. Dockerfiles and compose files updated
for the new agent service deps and CopilotKit dev sidecar.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires up an OpenAI-backed MAF agent that exposes WealthySmart
data through tool calls (recent transactions, cycle summary,
analytics, pensions). Pulls in agent-framework + AG-UI adapter
+ OpenAI client deps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ahorro was already deducted from gross salary so displaying it in
budget projections was misleading. This removes the Ahorro card,
summary line, Proyecciones column, and Ahorro Anual card from the UI,
and strips all savings fields from budget API responses.
Adds SALARY TransactionType so salary deposits can be distinguished
from generic DEPOSITO transfers. When a SALARY transaction arrives,
the system auto-increments MEMP and MPAT savings account balances
(+200K CRC each) once per month via an idempotent accrual log.
New CRUD endpoints at /api/v1/savings-accrual/ allow manual correction
of the accrual history. Feb+Mar 2026 are seeded as historical baseline.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Budget/transactions/salarios totals summed Transaction.amount directly,
so USD/EUR entries were treated as CRC and effectively disappeared from
the dashboard (the analytics fix in 9a80f2a only covered analytics).
Adds a shared get_converted_amount_expr() helper driven by the full
Currency enum — USD/EUR via ExchangeRate-API, BTC/XMR via CoinGecko —
and wires it into every func.sum(Transaction.amount) site.
Also starts a background task in the FastAPI lifespan that force-refreshes
every currency 4x/day, persisting USD to the DB and updating in-memory
caches for the rest. Failures are swallowed per-currency so a CoinGecko
outage cannot take out USD/EUR, and the last-known rate is always retained.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All three analytics endpoints (by-category, monthly-trend, daily-spending)
now convert foreign currency amounts to CRC using current exchange rates.
EUR/CRC rate derived from ExchangeRate-API (USD-based cross rate).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix SQLAlchemy case() import in monthly-trend endpoint. Add
data-sensitive attributes to Analytics charts and tables for privacy
blur. Add scripts/sync-db.sh for one-click prod-to-local PostgreSQL
sync. Remove SQLite artifacts from gitignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds deferred_to_next_cycle flag to transactions for billing cycle
bleed-over handling. Overhauls budget projection engine and refreshes
Budget page with improved monthly detail and transaction columns.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Existing account rows reference these values. Enum must stay compatible
with data in production. Only the frontend and seed data were cleaned up.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add paste-and-preview modal for entering pension fund balances from bank website
- Backend upsert logic so n8n PDF uploads overwrite manual entries
- Chart now shows actual snapshot data with dynamic month labels
- New POST /pensions/manual endpoint for JSON-based fund entry
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New BalanceOverride table for manual balance adjustments per month
- Cumulative balance computation with cross-year carryover
- Three new columns: Acum. Anterior, Neto Mes, Balance Acum.
- Inline editing on Balance Acum. cell (pencil icon for overrides)
- Year navigation clamped to 2026–2030, fresh start at March 2026
- PUT/DELETE /budget/balance-override/{year}/{month} endpoints
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DEPOSITO transactions (salaries) were displaying with negative sign and
red styling. Flipped logic so only COMPRA is negative; DEPOSITO and
DEVOLUCION both show as positive income.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend: parse BAC pension statement PDFs (VOL, ROP, FCL) via
pdftotext, store snapshots with duplicate detection, reject
credit card statements. Endpoints: POST /upload, GET /snapshots,
GET /fund-summary.
Frontend: wire up drag-and-drop upload, load real balances and
rendimientos from API, show upload results with error/duplicate
feedback.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Salary deposits were being counted as expenses in uncovered actuals,
causing negative balances. DEPOSITO transactions are income tracked
separately in the Salarios page.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New TransactionType.DEPOSITO for salary deposits from n8n/Gmail flow.
New /salarios endpoint with summary. New top-level Salarios page with
DataTable and summary cards. Push notifications link to /salarios for
deposits.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Budget: recurring items CRUD, yearly/monthly projections with no-double-count
logic, and full UI (overview, monthly detail, recurring items manager).
Push notifications: Web Push via VAPID keys, triggered on transaction creation
from n8n. Includes service worker handlers, frontend subscription flow, and
a test button on the Dashboard (temporary).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend now stores user settings (dashboard config) in a JSONB column and
exposes CRUD via /settings/. Exchange rate service gains multiple fallback
providers (ExchangeRate-API, currency-api, FloatRates) so USD/CRC rates
stay available when BCCR is down.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Expand Account model with account_type (pension, savings, liability, crypto), new banks/currencies (BTC, XMR, FCL, ROP, VOL, MEMP, MPAT, MORTGAGE), and next_payment field
- Add exchange rate endpoint (BCCR integration), analytics endpoint, paste-import for transactions, and API token management
- Add PWA manifest, service worker, and app icons
- Redesign dashboard, transactions, transfers, and login pages with theme support
- Add billing cycle selector, confirm dialog, and paste import modal components
- One-time DB reset in deploy workflow for schema migration
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend: FastAPI + PostgreSQL with models for accounts, transactions,
and categories. Auto-categorization from merchant patterns, token auth,
CRUD endpoints, and seed data for 16 categories and 4 bank accounts.
Frontend: Login, Dashboard (account balances + recent charges),
Transactions (full CRUD table with search/filter), Cash & Transfers
view. Dark theme with emerald/cyan accents, responsive layout.
Infrastructure: Updated docker-compose for backend + db services,
nginx proxy config for API routing, deploy workflow with secrets.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>