From faca4f4719330222bf52a03201a80741353a705b Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 29 Jul 2026 04:10:12 +0000 Subject: [PATCH] docs: give CLAUDE.md a codebase orientation layer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CLAUDE.md was a bare `@AGENTS.md` import, so Claude Code sessions loaded the full rule corpus with no orientation to the system those rules govern — stack, layout, and the two flows had to be rediscovered from docs/codebase-index.md every session. Keep the `@AGENTS.md` import (no policy is moved or duplicated) and add an orientation layer above it: what the product is, the instruction-file division of responsibility, the rules most easily violated by accident, stack/runtime, repository layout, the answer and ingestion flows, the verification pyramid, the conventions the gates enforce, and the repo-specific skills and subagents. AGENTS.md stays the single source of truth for rules; CLAUDE.md points at it rather than restating it, so the two cannot drift. Update the AI tooling map row in docs/agents-guide.md that described CLAUDE.md as a one-line import. Verified: prettier --check, docs:check-links, docs:check-scripts, docs:check-index all pass; every repo path and npm script referenced from CLAUDE.md resolves (it is outside the doc gates' scan set, so checked by hand). Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01PjFvJmVn1xb9QTtmuxcn7y --- CLAUDE.md | 199 +++++++++++++++++++++++++++++++++++++++++++ docs/agents-guide.md | 2 +- 2 files changed, 200 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 43c994c2d3..aa3ad0e38b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1 +1,200 @@ @AGENTS.md + +# Clinical KB — orientation for AI assistants + +Private, local-first medical guideline RAG knowledge base for a psychiatrist in Perth, +Australia. Clinical reference documents are uploaded to private Supabase Storage, indexed +(text + OCR + image captions) into pgvector, and answered with citations that link back to +the original PDF. + +This is a **clinical reference prototype, not validated clinical decision support**. Answers +must be verifiable against linked sources, and failure behaviour must always degrade +conservatively rather than guess. + +## How the instruction files divide up + +`AGENTS.md` is the single source of truth for **rules** and is imported above, so everything +in it is already in force. This file is the **orientation layer** — what the system is, how +it is laid out, how work flows through it. It deliberately does not restate AGENTS.md policy; +where the two ever disagree, **AGENTS.md wins**. + +| Need | Read | +| ------------------------------------------- | ---------------------------------------------------------------------- | +| Rules, gates, shortcuts, safety boundaries | `AGENTS.md` (auto-loaded) | +| Architecture, modules, schema, domain flows | `docs/codebase-index.md` — the deep map; start there for any real task | +| Routes and modes | `docs/site-map.md` (generated — `npm run sitemap:update`) | +| Which gate to run for a change | `docs/process-hardening.md`, or the `gates` skill | +| Test execution, focused/live, flake policy | `docs/testing.md` | +| Every maintained doc, categorised | `docs/README.md` | +| Outstanding work across sessions | `docs/outstanding-issues.md` (`/issues`) | + +When adding to this file, add **orientation**. Policy belongs in `AGENTS.md`; deep structure +belongs in `docs/codebase-index.md`. Keeping those three non-overlapping is what stops five +AI systems (Claude Code, Codex, Cursor, CodeRabbit, `.agents/`) from drifting apart — +see the AI tooling map in `docs/agents-guide.md`. + +## Highest-consequence rules + +Full text and rationale in `AGENTS.md`. These are the ones most easily violated by accident: + +- **Providers need explicit confirmation.** Never touch OpenAI, Supabase, GitHub/GitLab, + hosted CI, or any provider-backed workflow — including indirectly, via scripts, tests, or + release gates — without the user saying so. Prefer local/offline/mocked checks; report the + command and ask. +- **This is not the Next.js you know.** Next 16 has breaking changes versus most training + data. Read `node_modules/next/dist/docs/` before writing framework code. +- **RAG ranking surfaces are protected.** Flag the task _before_ editing anything under + `src/lib/rag/**`, clinical-search, retrieval-selection, ranking-config, answer-ranking, + the eval harness, or the golden fixture — even for a rename or a comment. Behaviour + changes need a live eval-canary pair. Read `docs/rag-behaviour/` first. +- **Supabase target is pinned.** Live project `Clinical KB Database`, ref + `sjrfecxgysukkwxsowpy`. The ref `qjgitjyhxrwxsrydablr` is stale — never use it. Migrations + target role `postgres`. +- **Check the review ledger before reviewing a branch or PR:** + `npm run ledger:lookup -- --scope ""`. Never read or hand-write + `docs/branch-review-ledger.md` — it is append-only and over a thousand rows. +- **Never assume `localhost:3000`.** Use `npm run ensure` and the URL it prints. +- **Evidence is never compressed.** Paste the decisive line from a gate. Exit code 0 alone + is not proof — `verify:ui` can exit non-zero on lock-contention timeout rather than + soft-skipping green. + +## Stack and runtime + +| Layer | Choice | +| --------- | ------------------------------------------------------------------------------------------ | +| Runtime | Node **24.x** / npm **11.x**, `engine-strict` (dev server exits on any other major) | +| Framework | Next.js 16 (App Router), React 19 | +| Language | TypeScript 6, strict; Zod 4 for env and request validation | +| Styling | Tailwind 4 (`@theme` tokens in `src/app/globals.css`), unlayered component CSS by design | +| Data | Supabase — Postgres + pgvector (HNSW), Storage, Auth; Edge Functions on Deno 2 | +| AI | OpenAI (embeddings, image captions, grounded generation) | +| Ingestion | Node worker + Python OCR (PyMuPDF / Tesseract) | +| Tests | Vitest (unit, `tests/**/*.test.ts`), Playwright (E2E, `tests/ui-*.spec.ts`) | +| Deploy | Railway project `Database` — `Database` (app) + `worker` services, auto-deploy from `main` | + +Install with `npm ci --include=dev`. Use `npm install` only when deliberately changing +dependencies. `npm install` also installs the repo's git hooks. + +**Demo mode:** with Supabase/OpenAI env absent, dev falls back to the synthetic corpus +(`src/lib/demo-data.ts`, `public/demo-documents/`) via `isDemoMode()` in `src/lib/env.ts`. +Production never silently falls back — missing config fails loudly. + +## Repository layout + +``` +src/app/ Next.js App Router — (search-app) route group, api/, auth/, mockups/ +src/components/ UI; clinical-dashboard/ is the shell, *-mockups.tsx are design scratch +src/lib/ ~200 modules — rag/, supabase/, validation/, observability/, + extractors/, webhooks/ are the extracted subdirectories +src/data/ Static clinical content (DSM, formulation, therapies indexes) +supabase/ migrations/ (source of truth), schema.sql (mirror), functions/ +worker/ Ingestion worker; worker/python/ is the OCR stack +scripts/ ~160 files backing 194 npm scripts: gates, eval, reindex, governance, dev +tests/ Vitest unit + Playwright E2E, side by side +docs/ Runbooks, governance, plans; docs/README.md categorises them +eslint-rules/ Repo-specific lint rules (see Conventions below) +.claude/ Claude Code agents, skills, hooks, settings +.agents/ Single-word skill catalogue (`npm run skills`) +``` + +Never commit: `.next/`, `node_modules/`, `coverage/`, `.env*`, `sample-documents/`, logs. + +The product surface is **13 app modes** (`src/lib/app-modes.ts`) sharing one search shell: +answer, documents, services, forms, favourites, differentials, dsm, specifiers, formulation, +prescribing, tools, therapy-compass, factsheets. + +## The two flows that matter + +**Answer (read path).** `/api/answer` → `src/lib/rag/rag.ts` orchestrates: hybrid retrieval +via Postgres RPCs (pgvector HNSW + tsvector/trigram) → `retrieval-selection` → +`answer-ranking` → routed OpenAI generation (fast vs strong) → `answer-verification` and +render policy → cited answer. If generation fails the quality gates it degrades to a +deterministic **source-only** answer that still cites real documents — that is expected +behaviour, not a bug. Responses cache in `rag_response_cache`. + +**Ingestion (write path).** `/api/upload` → private `clinical-documents` bucket + a row in +`ingestion_jobs` → `worker/main.ts` (or the `indexing-v3-agent` Edge Function) claims the +job → extract (PDF/DOCX/XLSX/TXT) → OCR fallback → image captioning → chunking → OpenAI +embeddings → chunks, pages, images, embedding fields, index units, table facts → quality +gates in `document_index_quality`. Reindex commits atomically per generation +(`reindex-pipeline.ts`). Lifecycle detail: `docs/ingestion-state-machine.md`. + +Both paths are owner-scoped: `owner-scope.ts`, `query-privacy.ts`, `authorization.ts`. + +## Development workflow + +```bash +npm run ensure # start/verify THIS project's dev server, prints the URL — never assume a port +npm run worker # local ingestion worker (second terminal) +npm run dev # direct dev server on the project-stable port +``` + +Verification pyramid — run the **smallest gate that covers the change**, then widen: + +| Gate | What it is | +| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `npm run test:focused -- --files ` | Source-only iteration. Fails closed for deleted files and test infrastructure — then run `npm run test`. | +| `npm run verify:cheap` | The broad local gate: 24 static/consistency gates + `lint` + `typecheck` + full offline unit suite | +| `npm run verify:pr-local` | Closest local mirror of the PR gate; adds format and conditional build / client-bundle scan / RAG fixture validation. `-- --dry-run --files ` shows selection without running. | +| `npm run verify:ui` | Chromium production journeys. Run `npm run ensure` first. | +| `npm run verify:phone-chrome` | Phone-chrome changes; selects affected owners/journeys before escalating to `verify:ui` | +| `npm run verify:release` | Full build + all browsers + readiness. **Provider-backed — needs approval.** | + +`verify:cheap` deliberately does **not** run `format:check`, which is why the installed +pre-push hook (`.githooks/pre-push` → `scripts/guard-push.mjs`) blocks on unformatted files. +It also guards the auto-merge race on `claude/*` branches and drift-manifest staleness. Each +guard has a documented override env var. + +Domain changes (auth, Supabase, ingestion, answer generation, search/ranking, clinical +output, source governance) additionally want the smallest relevant domain check plus +`npm run check:production-readiness`. + +CI (`.github/workflows/ci.yml`) is risk-scoped: a `changes` job classifies paths, `static-pr` +always runs, and `pr-required` is the single always-reporting required aggregate. Heavier +jobs (coverage, build, Chromium, Supabase migration replay, Docker builds) run only when +their file scope applies. + +## Conventions the gates enforce + +These fail builds, so they are worth knowing before you write code: + +- **Button wiring.** Every `