The previous symptom: agent succeeded on its 8th run_query (got 30
rows) but the loop ended without a final because that was the last
allowed hop. Result: "Stopped after 8 tool calls" and the data was
wasted. Also: agent kept assuming `legal_entities.name` existed even
after get_columns showed it didn't.
Backend (commands/chat.rs)
- MAX_HOPS 8 -> 10. With list_databases / list_tables / get_columns /
switch_database / run_query / remember / save_query / find_queries
available, complex investigations need a bit more headroom.
- New force_final_synthesis: when the loop falls through MAX_HOPS,
one extra LLM call is made WITHOUT the JSON action protocol,
asking the model to write a plain-text answer based on whatever
data was already collected. This rescues cases where the agent
succeeded on the last hop but had no budget for a final. Output
goes through clean_summary so any stray JSON or fences are stripped.
- Stronger RULES in system prompt:
* Explicit ban on guessing column names: "After get_columns, your
next run_query must use ONLY column names that appear verbatim
in that output."
* Concrete example of how to read PG's "column le.name does not
exist" — the alias `le` tells you which table is missing it.
* Mention the new hop budget (10) so the model spends it
deliberately.
Verification: cargo test --lib 70 pass, tsc clean.