# CourtMitra MissionOS Citizen-side grievance/legal-action autopilot for India. A user describes a problem once (WhatsApp / Telegram / browser); the system turns it into a **mission**, builds the evidence file, grounds it in Indian law/procedure, picks an official route, drafts + sends approved actions, follows up, and records the outcome. ## Prime directive **Complete the entire loop, automatically and honestly.** Intake → evidence → facts → cited route → consented action → real send → public case page → follow-up → reply → resolution → ledger. The reliability spine and safety gates exist to make that automated loop *trustworthy* — never polish a subsystem while the loop is broken. A demo that closes one real loop beats ten half-built modules. Deep specs live in (read on demand, do **not** load wholesale): - `courtmitra_missionos_full_idea.md` — product, UX, issue adapters. - `courtmitra_missionos_architecture_plan_v0_2_third_audit.md` — full architecture, data model (§9, §30), endpoints (§10, §30.15), state machine (§30.6). - Phase build specs (`PHASE_1_*.md`, `PHASE_2_*.md`) — **scope fence; build Phase 1 only** until told otherwise. ## Non-negotiable invariants (these prevent regressions) 1. **Hexagonal.** `packages/domain` imports NO framework/SDK (`openai`, `@nestjs/*`, `playwright`, qdrant, supabase, google/meta clients). It depends only on `packages/ports` interfaces. Adapters implement ports; `apps/api` wires them. 2. **Everything external is an Action.** Workflows NEVER call Gmail/WhatsApp/Telegram/Meta/Playwright directly. They create/execute an `Action`. The `ActionExecutor` is the only place side-effects happen, and it checks — in order — consent, idempotency key, adapter capability, rate limit, then sets `proof_state`. 3. **No fake submissions.** `proof_state` must reflect reality (`draft_only`, `ready_for_user_submission`, `sent_by_email`, `posted_to_social`, `user_confirmed_external_submission`, `receipt_verified`, `failed`). The UI must never imply more than `proof_state` supports. Indian gov portals have **no citizen write API** → packet + supervised browser + user-confirmed receipt only. 4. **No legal claim without a cited source.** Generated legal/route text must carry `cited_chunk_ids` + quote spans that **literally exist** in the source (exact substring check, not the model's self-report). No Tier-1/2 source → neutral admin language only, never presented as law. 5. **No public output without redaction + consent.** Run Presidio redaction before any share/publish. Consent is gated by a canonical `action_preview_hash`; if a regenerated payload's hash ≠ the approved hash, **block and re-request approval**. 6. **Contracts-first.** All LLM/API I/O is Zod schemas in `packages/contracts` (single source of truth). API routes and the frontend import these schemas — never redefine DTOs. OpenAPI is generated, not hand-written. 7. **Idempotent writes + webhooks.** Every write endpoint accepts `Idempotency-Key`; every provider webhook is deduped by provider event ID in `webhook_events` before processing. 8. **Every schema change is a Drizzle migration.** No ad-hoc DB edits. Seed route rules + curated citations. ## Locked tech stack — use these, not the alternatives | Layer | Use | Do NOT use | |---|---|---| | Monorepo | pnpm + Turborepo | — | | Web | Next.js (App Router) | — | | API | NestJS + Fastify | (don't mix in Express) | | DB / ORM | Postgres + Drizzle (Neon or local Docker; Supabase only with keep-alive) | raw SQL clients | | AI layer | **Vercel AI SDK 6** (`ToolLoopAgent`, tool-approval, structured outputs) behind `LlmPort`; Mastra optional | **OpenAI Agents SDK** (TS support lags — Python-first) | | RAG/search | **pgvector + Postgres FTS** + curated citations | Qdrant (Phase 2 upgrade) | | Evidence extraction | **vision-LLM** on screenshots/PDF images | Docling/Tesseract (Phase 2) | | Workflows | Inngest (durable sleeps + event fns). Payload ≤256KB → pass refs, not blobs | — | | Email | **Resend** (DKIM/SPF/DMARC) + reply token | raw SMTP; Gmail restricted scopes (CASA cost) | | Channels | WhatsApp Cloud API **+** Telegram Bot API **+** browser, all behind `ChannelPort` | single-channel coupling | | Auth | ONE of Supabase Auth / Better Auth + Google OAuth; channel identity first, browser merge via magic-link | SMS OTP | | Redaction | Microsoft Presidio + custom Aadhaar/PAN/UPI/IFSC recognizers | regex-only | | Storage / Obs | R2 or Supabase Storage / OpenTelemetry + Langfuse | — | ## Repo structure ``` apps/{web, api, worker} packages/{domain, ports, adapters, db, contracts, prompts, ui} ``` `domain` = entities/use-cases/policies (pure). `ports` = interfaces only. `adapters` = openai/whatsapp/telegram/resend/storage/meta. `contracts` = Zod + OpenAPI. `prompts` = versioned prompts + evals (no inline prompts in controllers). ## Commands (scaffolding must expose these; keep them green) ```bash pnpm dev # web + api + worker pnpm build # turbo build all pnpm typecheck # tsc --noEmit across packages pnpm lint # eslint incl. dependency-boundary rules pnpm test # vitest (unit + integration via Testcontainers) pnpm db:migrate # drizzle migrations pnpm db:seed # route rules + curated citations pnpm test:contracts # zod/openapi contract tests — MUST pass before UI work ``` Run `pnpm typecheck && pnpm lint && pnpm test:contracts` before considering any task done. ## Conventions - TypeScript strict; no `any` in domain/contracts. Validate every LLM output against its Zod schema; on failure, retry/repair, never pass through. - Errors: typed Result/exception at adapter edge; domain throws domain errors. Log with the trace ID that links message → mission → workflow → LLM call → action. - Naming follows the data model in architecture §9/§30 (snake_case tables, the documented state/event enums). Don't invent parallel names. - Append to `mission_events`; read UI state from `mission_status_snapshots` (don't recompute). - Money/dates in evidence: preserve original text + extracted value; never lose original-language proof. - Timezone: `Asia/Kolkata` for all deadlines. ## Safety & compliance (hard gates) - Approval-first: no send/post/share without a recorded consent grant whose hash matches the outgoing payload. - Public case pages are **private-link only** in Phase 1 (no live social, no indexing) — contains defamation/intermediary risk. Never label an entity "fraud/scam/criminal"; use "user reports / unresolved / alleged". - Stop-and-refer (emergency, criminal, domestic violence, child safety, active fraud) → `human_review_items`, halt automation, surface helpline guidance. - "Legal information & workflow assistance, not a lawyer-client relationship" disclaimer on legal outputs. Never guarantee outcomes. - DPDP: deliver consent notice at first contact; support data export + erasure; cross-user aggregation (swarm, Phase 2) needs its own consent basis. ## Model & delegation policy Default to the cheapest model that will not regress the task. Reserve expensive reasoning for where it matters. - **Haiku** (or the read-only Explore agent): file discovery, grep/lookups, mechanical transforms, dependency tracing, log scanning. - **Sonnet**: implementation — CRUD endpoints, adapters, UI components, tests, migrations, boilerplate from a clear spec. - **Opus** (main/advisor): architecture, data-model & schema design, the Action/consent/proof spine, RAG/legal-grounding logic, safety policies, ambiguous tradeoffs, and reviewing the above before merge. How to delegate: spawn subagents with explicit `model:` frontmatter (`haiku`/`sonnet`), or pass `model` to the Task tool. Keep menial, well-specified work off the main reasoning model. Escalate to Opus only when a task touches an invariant above or the spec is ambiguous. Always run an Opus/advisor review pass on anything touching consent, proof_state, redaction, or legal grounding. ## Scope fence Build **Phase 1** only. Do NOT start: complaint swarm, live social publishing, company resolver portal, Qdrant/rerank RAG upgrade, ASR/voice, Indic translation services, supervised Playwright portal automation, eCourts/CNR, or the AI-run public account. These are Phase 2 — see `PHASE_2_*.md`.