- Fix HP007/HP013: add 'drawdown', 'biggest holding', 'top holdings' to
performance keyword lists so these queries route to portfolio_analysis
- Fix MS005: use word-boundary regex for short city tokens (sf, atx, dfw)
to prevent 'sf' substring-matching inside ticker symbols like 'MSFT',
which was incorrectly routing to real_estate_snapshot
- Fix MS010: route full_report_kws to performance+compliance+activity
(was 'compliance' only, missing transaction_query for 'recent activity')
- Fix sc-004: add common 'portfolio' typos (portflio, porfolio, etc.) to
natural_performance_kws for robustness against misspellings
- Fix MS005 (part 2): add 'worth today', 'worth now', 'currently worth'
to market_kws so cost-basis-vs-current-price queries trigger both
portfolio_analysis and market_data
All eval suites now pass: 182/182 pytest, 60/60 run_evals, 25/25 golden sets
Made-with: Cursor
Follow-up questions like 'how does this compare to inflation?',
'is that good?', 'what does this mean?' were NOT being detected
as context_followup because the old check only had 8 hardcoded
phrases (e.g. 'how does that compare' but not 'how does this compare').
This caused every follow-up to fall through to 'performance',
call portfolio_analysis again, and return the same portfolio
summary regardless of what the user actually asked.
Added a broader _broad_followup_phrases list (35 patterns) covering:
- 'how does this/that/it compare'
- 'what does this/that mean'
- 'is that good/bad/normal/high/low'
- 'compared to inflation/the market'
- 'why is that', 'break that down', etc.
Confirmed with runtime logs: old_phrase_matched=false for the
follow-up, broad_phrase_matched=true, tools_used=[] (correct).
Made-with: Cursor
Remove 'total net worth' and 'everything i own' from wealth_net_worth_kws
so they fall through to property_net_worth_kws which runs property tracker
and shows the complete financial picture template.
Add 'show my total net worth' and related phrases to property_net_worth_kws.
Made-with: Cursor
Build pre-formatted financial picture template in property_net_worth executor.
Shows investment portfolio, real estate equity per property, total net worth,
and portfolio breakdown percentages. If no properties, prompts user to add one.
Template is passed as tool result so LLM presents it cleanly.
Made-with: Cursor
When user says 'add my home' without property details, return a warm
structured prompt asking for address, purchase price, current value,
mortgage balance, and monthly rent instead of calling add_property
with empty/zero values.
Detection: message lacks a price pattern (\$[\d,]+, \d+k, \d{5,}).
If message already contains a price, proceed directly to add_property.
Made-with: Cursor
Add _extract_strategy_params() helper to graph.py that parses
appreciation rate, buy interval, total years, home price, rent yield,
and annual income from natural language messages.
Update life_decision branch to detect strategy queries and route them
to simulate_real_estate_strategy() with extracted params instead of
calling the general life_decision_advisor. Supports conservative /
moderate / optimistic presets. Falls back to life_decision_advisor
for non-strategy queries.
Made-with: Cursor
- Expand classify_node keyword lists for relocation_runway, wealth_gap,
equity_unlock, and family_planner to cover more natural phrasings
- Add realestate_strategy_kws block that routes multi-property strategy
queries ("buy a house every N years", "rental portfolio strategy") to
the life_decision executor (home_purchase decision type)
- Expand real_estate_kws with MLS/market-data terms and add "show me"
to _location_intent_kws so city + intent triggers correctly
- Update _route_after_classify with explicit routing map comment showing
all 9 non-write categories → "tools" (only existing node names used)
- Add tool categories block to SYSTEM_PROMPT so LLM knows to use
non-portfolio tool results instead of defaulting to portfolio framing
Made-with: Cursor
- New agent/tools/wealth_bridge.py with 3 registered agent tools:
* calculate_down_payment_power(): maps portfolio value to 7 Austin markets
(or any cities) with can_afford_full/conservative/safe + monthly payment estimates
* calculate_job_offer_affordability(): COL-adjusted salary comparison for any
two cities worldwide using ACTRIS COL index (Austin) + Teleport API (global)
* get_portfolio_real_estate_summary(): reads live Ghostfolio portfolio + runs
down payment analysis in one call
- graph.py: add wealth_bridge imports + 4 new query_type routes:
wealth_down_payment, wealth_job_offer, wealth_global_city, wealth_portfolio_summary
- graph.py: add _extract_salary, _extract_offer_city, _extract_current_city helpers
- Mortgage formula: 30yr @ 6.95%, 20% down, ×1.25 for PITI
Made-with: Cursor
- graph.py: import real_estate functions (get_neighborhood_snapshot,
search_listings, compare_neighborhoods, is_real_estate_enabled).
- classify_node: detect real estate keywords → route to
real_estate_snapshot | real_estate_search | real_estate_compare.
Detection is fully guarded by is_real_estate_enabled() — when flag
is off, this block is never entered and existing routing is unchanged.
- tools_node: 3 new elif branches for real estate query types.
All append-only; no existing branches modified.
- Added _extract_real_estate_location() and _extract_two_locations()
helpers (new functions, no changes to existing helpers).
Made-with: Cursor
- eval runner: add retry logic (2 attempts) for transient connection drops
- gs-001: accept 'percent' as well as '%' (LLM formatting variance)
- gs-002: use must_contain_one_of for ticker/company name variance
- gs-008/sc-014: fix expected_tools for conditionally-triggered compliance
- graph.py: route 'health check'/'full report' queries to compliance path
so compliance_check always runs for full portfolio report requests
Co-authored-by: Cursor <cursoragent@cursor.com>
Source tags [tool_result_id] were appearing after every individual figure,
making responses unreadable. Rules 1 and 10 in SYSTEM_PROMPT and the
format_node user prompt now enforce one citation per sentence placed at
the end, not inline after each value.
Co-authored-by: Cursor <cursoragent@cursor.com>