Caught when a chip pointed to 1 World Way (Clifton A. Moore Admin
Building at LAX) and FlutIQ still produced residential-flavored
"For this property…" insurance copy, a homeowner action plan, and a
before-you-sign checklist — none of which make sense for a commercial
property. Root cause: property type wasn't surfaced anywhere; advisor
was hardcoded residential.
Fix (Option A — honest residential-only mode, on-brand with FlutIQ's
homeowner mission):
geocoder.py — classify Nominatim hits as residential or commercial
using class/type/name. Defaults to residential unless
the signal is confident; Census fallback assumes
residential (it only resolves TIGER street addresses).
orchestrator — pass property_type through into the dossier payload.
advisor — early-return for commercial: skip the LLM call entirely
and return a graceful note explaining FlutIQ is built
for residential and pointing to …
v0.14: regional_risk_agent — FEMA NRI multi-hazard at county level
Broadens FlutIQ from flood-only to multi-hazard. Every county in the US
now gets a calibrated risk profile across 18 hazards (wildfire, hurricane,
tornado, earthquake, drought, heat wave, etc.) at the neighborhood level
from FEMA's National Risk Index — the canonical source.
The win that matters: NRI's data was previously gated behind FEMA's
click-through CSV download. Found the exposed ArcGIS REST endpoint
behind RAPT (Resilience Analysis and Planning Tool):
https://services.arcgis.com/XG15cJAlne2vxtgt/arcgis/rest/services/
National_Risk_Index_Counties/FeatureServer/0/query
Free, no auth, point queries, 467 fields per county, full national
coverage. Verified live across 6 geographies with very different
hazard fingerprints:
Chicago: Cold wave + Winter weather + Tornado + Inland flood
LA: Earthquake + Wildfire + Inland flood (Community
Resilience: Very Low — interesting demo signal)
Houston: Hurricane + Tornado + Inland flood + Lightning
Miami:…
v0.13: Tier-1 multi-city expansion — Chicago + NYC + SF + LA + Austin
Refactors the Chicago-only local_agent into a city-aware system driven
by a registry. Each registry entry encodes the city's Socrata dataset
IDs, field names, flood-related category codes, and a context blurb
that gets injected into the local-agent prompt so Gemma 4 reasons
about each city's actual sewer system (combined vs separated, dominant
flood mode, local hazard pattern).
Honest per-city availability — what each city's dossier actually shows:
Chicago: full — 311 (WIB/SFL) + permits with reported_cost
(e.g. $80M new medical office at 4822 S Cottage Grove)
NYC: 311 — 391 sewer-flood reports per 350 5th Ave query.
Permits intentionally deferred: DOB datasets are
fragmented (legacy is stale, modern lacks dates,
active is filtered to currently-open jobs).
SF: full — 941 sewer reports + 75 permits at $366M (Mission
test point: '595 basement reports, increa…
v0.12: building permits as flood-risk leading indicator (Chicago)
The local agent now pulls TWO Socrata signals in parallel and asks
Gemma 4 to interpret them together: 311 flood reports (the historical
symptom) AND building permits (the leading indicator of impervious-
surface change). The combined-sewer system that handles stormwater
doesn't get upgraded when neighborhoods densify, so each new permit
shifts more load onto the same shared pipes.
The killer narrative this unlocks, verified live for 4521 S Drexel Blvd:
311 reports today: 0 (clean — historically stable)
Building permits: 89 within 1km / 3y, $189M total, including a
$80M new medical office and a $7.2M renovation
of a 15-story building literally next door.
Gemma 4 compound_signal: 'Clean 311 history combined with heavy
recent densification suggests a rising risk profile; the lack of
current reports may be a lagging indicator while the infrastructure
is being stressed by new development.'
That's leading-vs-lagging-indicator reasoning…
v0.10: 3-image multimodal — satellite + wider-area + street view in one Gemma 4 call
Risk analyst now reasons across THREE visual perspectives plus all
6 data sources in a single inference, exercising six Gemma 4
capabilities at once (vision + interleaved multimodal + reasoning
+ structured JSON + long context + graceful per-image fallback).
Pipeline:
Mapbox satellite (zoom 17, ~150m across) →
Mapbox outdoors (zoom 15, ~600m across) →
Google Street View (heading-aimed at the building) →
+ 6 data agents → risk analyst (Gemma 4 reasoning=on)
Verified end-to-end against 4521 S Drexel Blvd, Chicago. The model
named all three views in its trace ('From the satellite I can see…',
'In the street view…') and produced a visual_corroboration that
explicitly cross-referenced views vs data:
'The satellite view confirms a high percentage of impervious surface
due to the large asphalt parking lot, increasing runoff. The street
view corroborates the presence of basement-level windows, which
create a direct vulnerability to water ingress that is not reflected
…
The single deepest Gemma 4 capability we hadn't been using. Adds a
6th data agent that fetches a Google Street View image at the
property and asks Gemma 4 (vision) to identify visible flood-risk
indicators: basement-level windows, ground-floor HVAC, drainage
infrastructure, evidence of past water damage, elevation vs street.
Backend:
- New backend/app/tools/streetview.py — Google Street View Static
API client. Metadata-first pattern (free, doesn't count against
quota): walks radii None→50m→200m→500m looking for a real
panorama before fetching the image. Computes a bearing from the
panorama's actual location back to the geocoded address so the
camera aims AT the building (without this, default heading shows
the road, not the property — verified with a Drexel Blvd test
case where unaimed heading produced property_visible=false but
the aimed shot found a real impermeable-paving indicator).
Image returned as a data: URL ready to inline into the vision
request.
- New ba…
advisor: stop inventing insurance products — drive from a verified catalog
The advisor was generating insurance recommendations directly from
Gemma 4's training data, which produced two real inaccuracies in the
Chicago dossier:
- "NFIP Preferred Risk Policy $400-600/yr" — that product was retired
in October 2021 when Risk Rating 2.0 launched; there is no longer
a separate PRP tier.
- "Chicago DWM 312-747-7030" — wrong number. The real DWM line is
311 or 312-744-7000. (This came from the original spec; I propagated
it without verifying.)
For a tool whose mission is reducing the scariness of insurance,
making up products and phone numbers is the opposite of helpful.
Architectural fix:
- New backend/app/data/insurance_catalog.py — hand-curated list of
real, verified flood insurance products (sewer endorsement, NFIP
post-RR2.0, private flood via Neptune/Wright, parametric via
FloodFlash US/Arbol) and city-specific resources (Chicago: MWRD
rain barrels at correct $21.96, CNT RainReady, real DWM phone).
- Advisor agent now passes the catalog into…
geocoder: add US Census fallback when Nominatim returns nothing
Nominatim's US residential coverage is patchy — the user's
"1133 Potomac Road, Atlanta GA" returned [] from OSM despite being
a real address. The US Census Geocoder is authoritative for US
addresses (TIGER/Line) and free with no auth, so we fall back to
it whenever Nominatim has no match.
The /geographies/onelineaddress variant also returns county FIPS,
so downstream agents that key off county (archive, news location
queries) keep working. State is normalized back to full name to
match Nominatim's shape (so the local-agent's Chicago check still
fires correctly).
Verified: same address now returns
1133 POTOMAC RD, ATLANTA, GA, 30338 → 33.9337N, 84.3461W
city=Atlanta state=Georgia county=DeKalb County
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
backend: full 7-agent pipeline working end-to-end
Adds the four remaining data agents (local/weather/news/archive),
the risk-analyst agent (Gemma 4 reasoning mode), and the advisor
agent. Orchestrator runs the 5 data agents in parallel, then risk,
then advisor, streaming agent_update SSE events along the way and
emitting the compiled dossier on complete.
Verified against 4521 S Drexel Blvd, Chicago — full assessment in
~30s, all 7 agents return structured JSON, and the risk agent's
reasoning_trace shows actual step-by-step math:
P = 1 - (1 - 0.005)^30
P = 1 - (0.995)^30
0.995^30 ≈ 0.860
P ≈ 0.14 (14%)
It also unit-converted the precipitation forecast (1.02 mm/hr ≈
0.04 in/hr) and compared to the 0.67 in/hr Chicago combined-sewer
overwhelm threshold from the system prompt before concluding low
near-term risk. Advisor wrote recs at 5th-grade level as instructed.
Spec deviations:
- archive_agent uses GDELT 24-month timespan as a Storm Events DB
proxy. NCEI Storm Events has no clean public JSON API (CSV bulk
per year only)…
Stands up the FastAPI backend with one agent fully wired through the
SSE pipe so the architecture is proven before we fan out the rest:
POST /api/assess → geocode → FEMA NFHL → Gemma 4 interpret
→ agent_update events → complete{dossier}
Verified end-to-end against two cases:
- Houston Allen Pkwy (Zone AE) → is_sfha=true, requires_insurance=true
- Chicago S Drexel (Zone X) → gap_warning generated unprompted,
surfacing the urban-sewer-backup blind spot that is the whole pitch
Smoke-tested Gemma 4 on OpenRouter free tier (scripts/smoke_test.py):
basic completion, reasoning mode, and OpenAI-format tool calls all
pass on google/gemma-4-31b-it:free.
Spec bugs fixed while building:
- llm/client.extract_reasoning read message.reasoning_details[i].content;
the actual key is .text. Also falls back to top-level .reasoning string.
- tools/fema requested VERSION_ID, which does not exist on NFHL layer 28.
ArcGIS returned HTTP 200 with an {"error": ...} b…