AGENTS.md — Rules for AI agents working on Mapvest
Anyone touching this repo (Claude Code, Codex, Cursor, Devin, human‑with‑agent) reads this first. It is intentionally short and load‑bearing.
1. What Mapvest is
A map + camera product that turns places and objects into investable tickers. If the brand is public → give the ticker. If it's private → give the closest public comparable and an ETF with real exposure. Nothing else in the codebase matters if that loop is broken.
2. Non‑negotiables
- No secret ever hardcoded. All secrets flow through Doppler personal workplace (jawaun personal). Mapvest uses project
mapvest(dev/stg/prd). Sibling Railway apps each have their own project; identical provider tokens live inshared. Not GICcofounder. Read viadoppler run -- ...in scripts or viaprocess.env.*populated by Doppler. If a secret has to reach the iOS app, hash/mask it viajqbefore writing to the runtime bundle — seedocs/SECRETS.mdandinfra/doppler/README.md. - API layer vs implementation layer is a hard boundary.
apps/apiexposes HTTP; it must not import fromapps/iosorapps/landing. Everything shared lives inpackages/*. Any downstream web/mobile client can plug in without changes toapps/api. - Types are the source of truth. Every request and response is a zod schema in
packages/core/src/schemas. Never invent a JSON shape at the call site. - Never fake financial data. Ticker resolution, ETF matches, and comparables always cite a source (Exa result URL, provider name, timestamp). If confidence is low, return
confidence: "low"and let the client decide. - User photos are private by default. Never persist a raw uploaded photo to a public bucket. If storage is needed, use a signed URL bucket keyed by user id.
- Docs live in
.mdand are served by the landing page. If you change behavior, update the relevant doc in the same PR. The landing page readsdocs/*.mdand renders them.
3. Layout you must respect
apps/api Bun + Hono. Only HTTP + auth + rate limiting. No business logic beyond glue.
apps/ios Expo React Native. Camera, map, list, login, admin.
apps/landing Next.js. Marketing + rendered docs.
packages/core Shared types + zod schemas. No runtime deps beyond zod.
packages/vision OpenRouter multimodal client (GPT-5.6 Terra / Claude Opus 4.8 / Grok 4.6). Input: image bytes. Output: {brand, product, confidence, tags}.
packages/finance Ticker resolver, private→public comparable, ETF match. Cites sources.
packages/search Exa wrapper for open-web enrichment.
infra/railway Railway service configs.
infra/doppler Doppler mount snippets.
Do not add a package outside packages/. Do not add an app outside apps/. If a new concern doesn't fit, propose the shape in IMPLEMENTATION_PLAN.md before coding.
4. Tooling
- JS runtime: Bun 1.3+.
bunfor install,bun runfor scripts,bun testfor tests. - Yarn is allowed only inside
apps/iosif Expo forces it. Nowhere else. - TypeScript everywhere except Swift bridging (avoid unless we ship a pure‑Swift native module).
- Env: Doppler. Never write
.envfiles that contain real secrets..env.exampleis fine. - Lint/format:
biomeat the repo root.
5. Secrets contract
Every secret this repo touches is in Doppler mapvest/dev (or stg / prd) in the personal workplace. The names below are the exact env var names — do not rename:
| Purpose | Env var |
|---|---|
| Multimodal LLM (image + text) | OPENROUTER_API_KEY, OPENROUTER_BASE_URL |
| Open‑web search | EXA_API_KEY |
| Gemini direct (fallback vision) | GEMINI_API_KEY |
| Google Maps / Places (server side) | GOOGLE_APPLICATION_CREDENTIALS_JSON and/or a scoped GOOGLE_MAPS_API_KEY (add if missing) |
| Anthropic (agent operations only, not user path) | ANTHROPIC_API_KEY |
For iOS, the maps SDK key is delivered via a Railway‑signed short‑lived token endpoint — the client never sees the raw Google key. See docs/SECRETS.md for the hash‑and‑forward pattern.
6. Data source contract
Every finance answer must attach a sources: Source[] array. The canonical shape lives as a zod schema in packages/core/src/schemas and is projected into the OpenAPI 3.1 document at the repo root (openapi.yaml) — see the Source component there for the authoritative field list. For reference:
type Source = {
provider: "exa" | "openrouter" | "gemini" | "yahoo" | "polygon" | "sec" | "manual";
url?: string;
fetchedAt: string; // ISO
confidence: "high" | "medium" | "low";
};
If you cannot cite a source, return an empty sources: [] and set overall confidence: "low" — do not fabricate.
API contract artifacts. openapi.yaml and postman.json at the repo root are generated files — never hand-edit. Regenerate whenever a schema in packages/core changes:
bun run openapi # zod → openapi.yaml
bun run postman # openapi.yaml → postman.json
Downstream clients (iOS, landing, external integrators) consume openapi.yaml as the wire contract; the zod schemas remain the source of truth for the API implementation itself.
7. How to run and test
# from repo root
doppler setup --project mapvest --config dev
bun install
bun run dev # api :3001, landing :3000, expo :8081
bun test # runs all package + api tests
For the iOS app specifically:
cd apps/ios
bun install
bun run ios # launches simulator
8. Deploy
- API + landing → Railway (
infra/railway). - iOS → TestFlight via EAS (
apps/ios/eas.json). - Never deploy from a dirty tree. Never deploy
mainwithout CI green.
9. Agent workflow rules
- Read
IMPLEMENTATION_PLAN.mdfirst, then this file, then the doc for the area you're touching. - One concern per PR. If your change spans map + camera + finance, it's three PRs.
- Update docs in the same commit as the behavior change.
- Never disable a failing test to "unblock" — fix it or write a follow‑up task.
- Never push secrets, .DS_Store, node_modules, or ios build artifacts.
- Cite sources in commit messages when the change is finance‑adjacent ("uses Exa result at <url>").
- When you finish a phase in
IMPLEMENTATION_PLAN.md, tick its checkbox in the same commit.
10. Escalation
If the current instruction contradicts this file, this file wins — and open an issue tagged agents-md-conflict so it can be reconciled. Do not silently deviate.