Prod showed the failure mode after the date fix: get_current_date
returned the right day, but the model answered "¿cuánto he gastado
hoy?" from cycle-level aggregates (the only spend tools it had) and
concluded ₡0. Give it a day-grained tool — per-day CRC totals mirroring
/analytics/daily-spending (COMPRA only, no installment anchors, no
future cuotas) — and a prompt rule to match tool to time grain.
Verified: fresh thread now calls get_current_date →
get_daily_spending(2026-07-04, 2026-07-05) and answers correctly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two bugs from prod (server clock is UTC, user is UTC-6):
- get_recent_transactions returned future-dated Tasa Cero cuotas; it now
excludes anything dated after the user's today, same rule as
/transactions/recent.
- "¿Cuánto he gastado hoy?" answered for the wrong day: the prompt baked
date.today() in at boot — frozen from startup AND in server-UTC (8 pm
in Costa Rica is already tomorrow in UTC). The prompt no longer carries
a date; a new get_current_date tool returns today in CR time plus the
active billing cycle, and the prompt directs the model to call it for
any relative date reference. today_cr() lives in timeutil (CR has no
DST, fixed UTC-6).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
After the client returns a render tool's result, the model sometimes
re-issues the identical call with a fresh id on the follow-up run,
painting a second copy of the spending-summary card (observed live on
MAF 1.10 / CK 1.62). DeduplicateToolCallMiddleware now tracks which
render tools already ran in the current user turn and drops repeats —
their streamed TOOL_CALL_* events and their entries in the run's
MESSAGES_SNAPSHOT. The system prompt also tells the model the card is
already displayed once its "ok" result is present.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Back MAF's opt-in AG-UI snapshot persistence with a DB store so the
conversation survives page reloads and restarts:
- ChatThreadSnapshot: one upserted row per (scope, thread_id), JSONB
messages/state/interrupt (migration 794baf50635b)
- PostgresSnapshotStore implements the AGUIThreadSnapshotStore protocol.
It opens its own sessions (MAF saves during SSE streaming, after the
request middleware has closed the ContextVar session) and sanitizes on
save: camelCase toolCalls/toolCallId (the @ag-ui/client Zod schema
strips snake_case, which would lose cards on replay), drop BFF ctx-*
context messages, dedupe MAF's re-recorded multi-run turns, and drop
orphaned tool results
- Endpoint mount gains snapshot_store + a constant scope resolver
(single-user app; auth already enforced by agent_auth_and_session)
- DELETE /api/v1/chat/thread resets the conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
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>
- 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>
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>
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>