# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

```bash
# Dev server (hot reload)
npm run dev

# Type check (no emit)
npm run check

# Build for production
npm run build

# Unit tests for paradigm-generator only (fast, no browser)
npx tsx server/paradigm-generator.test.ts

# Playwright E2E (requires running app)
npm test                    # chromium only
npm run test:all            # chromium + mobile + setup

# DB schema push (Drizzle)
npm run db:push
```

## Deploy

All code changes require a Docker rebuild. From the repo root:

```bash
cd /var/lib/docker/volumes/openclaw_workspace/_data/classicslens
docker compose -p lector build && docker compose -p lector up -d
docker logs lector-app -f   # verify startup
```

Restart without rebuild: `docker compose -p lector up -d`

DB shell: `docker exec -it lector-postgres psql -U lector -d lector`

Env vars live in `.env` at the repo root (gitignored). `COMPOSE_PROJECT_NAME=lector` must be set there so docker compose uses the right project name.

## Architecture

```
nginx (SSL termination)
  └── lector-app  port 5001 → 5000  (Express + React SPA)
        ├── morphology.db        SQLite, ~500MB, read-only, volume-mounted
        ├── lector-postgres      PostgreSQL 16 (users, passages, paradigms, SRS, etc.)
        └── morpheus-api         Python sidecar, port 5100, internal only
```

**Parse lookup — three-tier fallback:**
1. SQLite `morphology.db` — exact Unicode, then diacritics-stripped bare match (~1ms)
2. Morpheus sidecar — real-time analysis, generates all accent variants for accentless Greek (~100–500ms)
3. In-memory tables in `server/data/inflections.ts` — last resort

The `source` field in parse responses indicates which tier responded.

## Key Server Files

| File | Role |
|------|------|
| `server/paradigm-generator.ts` | Rules-based paradigm generator — 3500+ lines, all Latin and Greek |
| `server/paradigm-db.ts` | Seeds paradigms into PostgreSQL at startup; on-the-fly Greek noun generator |
| `server/morphology-db.ts` | SQLite lookup layer — `lookupWord`, compound detection, Morpheus integration |
| `server/routes.ts` | All Express routes; builds the `lemmaByHeadword` map at startup |
| `server/data/lemmas.ts` | Curated Latin + Greek lemma store (principal parts, defs, frequency ranks) |
| `server/data/greek-verbs.ts` | Bulk Greek verb headwords from morphology DB (for paradigm seeding) |
| `shared/schema.ts` | Drizzle schema + shared TypeScript types |

## Paradigm System

Paradigms are pre-seeded into PostgreSQL at startup by `seedParadigms()` in `paradigm-db.ts`. **Seeding order matters** — hardcoded/correct tables must be inserted first; `ON CONFLICT DO NOTHING` protects them from being overwritten by wrong generator output.

Seeding order:
1. Latin irregular verbs (`LATIN_IRREG_VERB_HEADWORDS`)
2. Curated lemmas (macronized headwords from `lemmas.ts`)
3. Whitaker's DICTLINE bulk verbs, deponents, nouns
4. Greek suppletive verbs (`GREEK_SUPPLETIVE_HEADWORDS`) ← must precede curated Greek
5. Curated Greek verbs (from `greekLemmas` with `principalParts`)
6. Bulk Greek verbs from `loadGreekVerbs()` (present-system only if no PPs)
7. Greek -μι verbs (`GREEK_MI_VERB_HEADWORDS`), pronouns, article, adjectives, nouns

**Hardcoded table types in `paradigm-generator.ts`:**
- `-μι verbs`: `GREEK_MI_VERB_TABLE` — εἰμί, φημί, δίδωμι, τίθημι, ἵστημι, ἵημι. Use `miFinite`/`miNonFinite`/`makeMiRow` helpers. Accents are pre-applied (non-recessive); do not use `recessiveAccent`.
- **Suppletive verbs**: `GREEK_SUPPLETIVE_VERB_TABLE` — φέρω, λέγω, ἔρχομαι, ὁράω. Same helpers.
- **Latin irregulars**: `LATIN_IRREG_VERB_TABLE` — sum, possum, volō, nōlō, mālō, eō, ferō, fīō, ōdī, auferō.

**Adding a new Greek irregular/deponent verb:**
1. Add a hardcoded `const GRK_VERBNAME: ParadigmRow[]` table using `miFinite`/`miNonFinite`/`makeMiRow`.
2. Add it to `GREEK_SUPPLETIVE_VERB_TABLE` (or `GREEK_MI_VERB_TABLE` for -μι).
3. Add a lemma entry to `greekLemmas` in `server/data/lemmas.ts` with `principalParts`.
4. Rebuild and redeploy — seeding runs at startup.

**Adding a regular Greek verb with principal parts:**
1. Add a lemma to `greekLemmas` with all available principal parts.
2. `generateGreekVerbFromPrincipalParts` will handle it automatically at next startup seed.

**Suppletive paradigm aliases** (in `routes.ts`): Morpheus sometimes returns a suppletive stem as the lemma (e.g. `εἶδον` for the aorist of `ὁράω`). The `SUPPLETIVE_PARADIGM_ALIASES` map in `routes.ts` redirects bare stripped forms to the canonical headword stored in the paradigms table.

## Frontend

React 18 + Vite + Tailwind CSS + shadcn/ui. Routing via `wouter` (hash-based). Entry: `client/src/main.tsx`. Pages in `client/src/pages/`. No SSR.

API calls use `@tanstack/react-query`. All API routes are prefixed `/api/`.

## Greek/Latin text handling

- Always normalize to NFC before DB queries (`lemma.normalize('NFC')`).
- Diacritics stripping: `.normalize('NFD').replace(/[̀-ͯ]/g, '')` — used everywhere for bare-form matching.
- Morpheus returns Beta Code; the sidecar converts to Unicode.
- Accent placement on generated forms: use `recessiveAccent(stem + ending)` for regular thematic verbs; hardcode accents for -μι and suppletive verbs.
