# Next Session: Add Weather & Air Quality Tools + Fix Nearby Search

## What

Add `get_weather` and `get_air_quality` tools to Annie, using Google's Weather API and Air Quality API. Also fix the broken nearby search (`REQUEST_DENIED` from legacy Places API). All three use the same `GOOGLE_MAPS_API_KEY` already configured on Titan. These are among the most natural things to ask an assistant — "What's the weather?" and "Is it safe to go for a run?"

## Plan

Path: `~/.claude/plans/enchanted-meandering-stroustrup.md`

**Read the plan first** — it has the full implementation, all 17 adversarial review findings (all addressed), and design decisions. Two rounds of hostile architecture + code quality review were applied.

## Key Design Decisions (from adversarial review)

1. **Step 0 is MANDATORY** — curl Weather and AQI APIs live BEFORE writing any code. Session 13's Places API was "enabled" but still returned REQUEST_DENIED. Don't repeat that. If an API returns errors, skip that tool.
2. **AQI index selection** — iterate `indexes[]`, prefer `code == "ind_cpcb"` (India CPCB standard), fall back to universal AQI. Do NOT use `indexes[0]` blindly — wrong AQI for India is a health risk.
3. **TTL cache on geocoding** — `cachetools.TTLCache(maxsize=64, ttl=300)` on `_geocode_location()` to avoid double geocoding when weather + AQI are queried together.
4. **Field-boundary truncation** — don't slice at 400 bytes (cuts mid-word for TTS). Build response in priority order, stop adding fields at the limit.
5. **Semantic error check in `_google_api_post()`** — check for `"error" in data` (HTTP 200 with error body). Google new APIs do this.
6. **Guard `_geocode_location()` against raw "home"** — reject `_HOME_ALIASES` to force callers through `_resolve_location()` first.
7. **Empty response guard** — if all extracted fields are None, return "data format unexpected" + log warning instead of silent empty output.
8. **No voice channel** — `google_maps` itself is not in bot.py's `_BASE_TOOL_SCHEMAS` (pre-existing gap). New tools follow same pattern: text/Telegram/phone/WhatsApp only.
9. **Separate timeout** — `_WEATHER_TIMEOUT_S = 4.0` for fast lookups, vs `_API_TIMEOUT_S = 8.0` for routing.
10. **Word-boundary regex** — `\bget_weather\b|\bget_air_quality\b` in `_TOOL_LEAK_RE` to avoid false positives.

## Files to Modify (in order)

1. `services/annie-voice/maps_tools.py` — Constants, `_geocode_location()` with TTL cache, `_google_api_post()`, `get_weather()`, `get_air_quality()`, handler wrappers, refactor `search_nearby()`
2. `services/annie-voice/tool_schemas.py` — `WeatherInput`, `AirQualityInput`
3. `services/annie-voice/tool_adapters.py` — `WeatherAdapter`, `AirQualityAdapter`, ADAPTERS registry
4. `services/annie-voice/text_llm.py` — Import handlers + schemas, 2 new ToolSpecs
5. `services/annie-voice/bot.py` — `_TOOL_LEAK_RE` update, narration phrases
6. `services/annie-voice/tests/test_maps_tools.py` — 36 new tests (phases 5-10)
7. `services/annie-voice/tests/test_tool_adapters.py` — 12 new tests
8. `services/annie-voice/requirements.txt` — `cachetools>=5.3` (if not already present)

## Start Command

```
cat ~/.claude/plans/enchanted-meandering-stroustrup.md
```

Then implement the plan. All adversarial findings are already addressed in it. Start with Step 0 (live curl verification) — this gates all subsequent work.

## Verification

1. **Step 0 curl tests** — verify Weather and AQI APIs return valid JSON (GATE)
2. **Local tests** — `python3 -m pytest tests/test_maps_tools.py tests/test_tool_adapters.py -v`
3. **Deploy** — `./stop.sh annie-voice && ./start.sh annie-voice`
4. **E2E Weather** — `curl /v1/chat "What is the weather like right now?"` → temperature + conditions
5. **E2E AQI** — `curl /v1/chat "Is it safe to go for a run outside?"` → AQI + health recommendation
6. **E2E Nearby** — `curl /v1/chat "Find coffee shops near HSR Layout"` → 3 places (if fixed)
7. **Key leak check** — `grep "AIzaSy" /tmp/annie-voice.log` → 0 matches
8. **Capability manifest** — `curl /v1/capabilities` → includes `get_weather`, `get_air_quality`
