# CLAUDE.md — Ooru agent operating guide

Agent context for working in this repo. Read this before touching code.
Companion: `docs/AGENTS.md` (same content, generic-agent framing),
`docs/NOTES.md` (full audit + competitor analysis — the durable reference).

---

## What this is

Ooru is a Bangalore house-hunting assistant. Landmark in → geocode → nearby
metro + ICSE schools → rank alternative localities by AQI → (intended) rental
listings → LLM advisory report. Two surfaces: CLI (`chatbot.py`) and FastAPI
(`backend/app.py`).

## Current direction — v2 (read before building on v1)

The architecture verdict (docs/architecture.html §04, docs/MIGRATION.md) is to
REPLACE the v1 stack below with the og-coach PocketBase pattern: one `pocketbase`
binary + one `pb_hooks/main.pb.js` hook (sequential calls, server-only LLM key) +
`pb_migrations/` + a vanilla `pb_public/` PWA + `tools/deploy.sh`. This kills both
v1 bugs by construction — the fabrication bug (one linear hook assembles only
verified fields, no `return {}` to drop data) and the hardcoded-JWT-secret leak
(PocketBase auth is built-in). Target signal flow: geocode → schools → metro → AQI
→ rank → LLM. v2 is NOT built yet; v1 still runs. When implementing, follow
MIGRATION.md, not the v1 stack — the v1 detail below is the audit being migrated
away from, kept for reference. Reference data sources/endpoints: docs/DATA_SOURCES.md
§8 (OSM Overpass, Nominatim/Ola, Open-Meteo + CPCB, Apify; NO Google scraping).

## Stack

- FastAPI (REST + JWT/bcrypt auth)
- SQLAlchemy (NOT Prisma) — `User`, `HouseRental`, `APICache` in `backend/database.py`
- LangGraph — orchestration graph in `workflow.py`
- OpenAI SDK → Moonshot Kimi K2.6 (advisory; simulation fallback)
- Mapbox (geocode v5 + matrix), Foursquare (places), Open-Meteo (AQI), Apify (scraping)
- Python. Local toolchain: uv + Python 3.11.

## File map

    main.py            external API integrations (the data layer) + print-heavy logic
    workflow.py        LangGraph nodes/edges wrapping main.py functions
    chatbot.py         CLI loop + Kimi streaming + simulate_kimi_response fallback
    backend/app.py     FastAPI routes (register, token, login, chat, history)
    backend/services.py auth, JWT, password hashing, execute_chat_workflow, persistence
    backend/database.py engine + SQLAlchemy models + APICache cache helpers
    backend/models.py  Pydantic request/response SCHEMAS (misnamed)
    backend/schemas.py DEAD — unused duplicate of models.py. Slated for deletion.
    test_api.py        e2e API flow (bare asserts, hits live APIs + real DB)
    test_chatbot.py    subprocess CLI integration test
    docs/              this guide + AGENTS.md + NOTES.md + *.html strategy artifacts

## THE non-negotiable rule: data honesty

The LLM advisory must only state findings the pipeline actually computed.

Current bug (do not replicate, fix when asked): `find_nearby_schools` and
`find_nearby_places` only `print()`; their nodes `return {}` (workflow.py:33-52),
so schools/metro never enter GraphState and never reach the LLM prompt
(services.py:139-146). The report's "ICSE schools nearby / metro under 3km" lines
are hardcoded boilerplate (chatbot.py:31-39) — fabricated. This is the #1 issue.

When you add or edit any report-generating code: every claim must trace to real
state. If data is absent, say so explicitly ("no schools found within X m") — never
fill the gap with confident boilerplate.

## Conventions

- Python 3.11, create venvs with `uv venv --python 3.11 .venv` then `uv pip install`.
- Every `requests.get()` needs a `timeout=`. (Currently missing everywhere — a known gap.)
- Use the `APICache` layer for any new external call (api_name + cache_key pattern).
- LangGraph nodes are thin wrappers that RETURN dict updates into GraphState. A node
  that does work but returns `{}` is almost always a bug (the schools/metro failure mode).
- Secrets come from env only. Never hardcode a default secret (services.py:22 currently
  violates this — fix, don't copy).
- No Alembic yet — schema is `Base.metadata.create_all()` at import. Prefer extending the
  `top_matches` JSON column over new columns until migrations exist.
- Tests are not pytest-structured and hit live APIs/DB. Don't assume hermetic test runs.

## Commands

    # setup
    uv venv --python 3.11 .venv && source .venv/bin/activate && uv pip install -r requirements.txt
    # CLI
    python chatbot.py            # interactive
    python workflow.py <landmark># run graph directly
    # API
    uvicorn backend.app:app --reload
    # tests (need DATABASE_URL + live API keys; not hermetic)
    python test_api.py
    python test_chatbot.py

## Working agreement

- Confirm scope before multi-file edits. State exact files you will touch.
- Do NOT make code changes unless the user approves a specific roadmap item
  ("proceed with [item]"). The roadmap lives in docs/NOTES.md §8 and docs/product.html.
- Honest negatives matter. Report blockers; never fabricate tool output or results.
- Keep status terse. Never compress the actual technical explanation, numbers, or caveats.

## Roadmap pointer

Horizon 0 (do first): 0.1 wire schools+metro into state+prompt (honesty fix),
0.2 JWT secret from env, 0.3 .gitignore + untrack .env/__pycache__ + rotate keys,
0.4 delete dead code, 0.5 request timeouts, 0.6 pin deps, 0.7 non-blocking /chat.
Single highest-leverage step: 0.1. Full detail: docs/NOTES.md and docs/product.html.

## .gitignore note

Installed design skills live in `.agents/ .claude/ .cursor/ .gemini/ .opencode/`
inside this repo. These plus `.env`, `__pycache__/`, `.venv/` should be gitignored
(part of Horizon-0 item 0.3).
