# CLAUDE.md

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

## Repository status

**Phase 0–5 (M1–M5) are all shipped; there is no active phase.** The monorepo is scaffolded and the full MVP loop runs end-to-end: 立项 → 设定库 → 大纲 → 写章(SSE) → 四审(一致性/伏笔/文风/节奏) → 验收事务，底层带多 provider 网关(路由/回退/熔断) + 声明式 skill 注册表(§5.5) + Kimi Code OAuth(K1)。Last green gate: backend ruff/format clean · mypy **154 files** · alembic **no drift** · pytest **434 passed**; frontend lint/typecheck clean · vitest **155 passed** · build OK. **The build/test tooling exists — see §Toolchain below; do not re-scaffold.**

`PROGRESS.md` is the authoritative status ledger (current phase + archived M1–M5 detail + changelog). Read it before any task. **Next work if resumed**: DEV_PLAN §4 P2/后续 (向量检索 / 真任务队列 / 全书一致性扫描 / 社区市场), or the 对标竞品功能补齐 plan (`~/.claude/plans/streamed-giggling-fiddle.md` — AI续写/扩写、AI拆书、通用生成器框架、细纲、模板库).

Product: an AI-assisted **Chinese web-novel writing workflow**, delivered as a web app. The core thesis (`PRODUCT_SPEC.md §1.5`) is to treat long-form novel writing like software engineering — establish architecture (worldbuilding/characters/outline), generate against spec, test every chapter, maintain a single source of truth.

## Documentation map — read the right doc

The four specs are layered. Read in this order; each later doc refines the earlier.

| Doc | Answers | Read when |
|---|---|---|
| `PRODUCT_SPEC.md` | What & why — problem, features (+priorities P0/P1/P2), data model, endpoints, agents | Understanding scope or a feature |
| `UX_SPEC.md` | What it looks like — IA, user flows, page wireframes, components, visual tokens (纸感/warm-cream theme) | Building any UI |
| `ARCHITECTURE.md` | How to implement — full DDL, LLM gateway internals, orchestration engine, API contracts, cross-cutting concerns | Implementing backend/data/gateway |
| `DEV_PLAN.md` | In what order — Phase 0–5, decoupled tasks each tagged with a discipline skill (`@backend`/`@frontend`/`@db`/`@llm`/`@devops`/`@qa`) | Picking the next task |

`ARCHITECTURE.md` sections are anchored to `PRODUCT_SPEC.md` sections (`← PRODUCT_SPEC §x`). DEV_PLAN tasks reference both.

**冲突裁决**：若两份 spec 出现矛盾，以更具体/更靠后的文档为准（`ARCHITECTURE` > `UX_SPEC`/`DEV_PLAN` > `PRODUCT_SPEC`），并回写修正上游文档以消除分歧（见下文 §Conventions 的 spec 一致性纪律）。具体的枚举值（DDL 字段、SSE 事件、日志字段、错误码、§x.y 锚点）以 ARCHITECTURE/PRODUCT_SPEC 为唯一真源——本文件只重述跨文档、易错的**规则**，不复制会变的清单。

## Locked tech stack — do not relitigate

These were decided after extended discussion (`DEV_PLAN.md §0`). Honor them; do not reintroduce the rejected alternatives.

- **Frontend**: Next.js + TypeScript (UI only; calls the backend via an OpenAPI-generated TS client — **no hand-written shared types, no business logic in Next API routes**).
- **Backend**: Python + **FastAPI** (async, SSE).
- **Orchestration**: **LangGraph** (the write→review→accept graph).
- **LLM access**: a **thin self-built gateway** over `anthropic` + `openai`(baseURL covers DeepSeek/Kimi/Qwen/GLM) + `google-genai`. **Not** LangChain, **not** vendor CLIs/managed-agent runtimes.
- **Structured output**: Pydantic + `instructor`.
- **ORM/migrations**: SQLAlchemy 2.0 (async) + Alembic.
- **Storage**: Postgres. **No pgvector** in the prototype.
- **Long tasks**: FastAPI BackgroundTasks + a `jobs` table. **No dedicated queue.**

**Prototype scope — explicitly deferred (do not build unless asked):** multi-tenancy / auth (prototype is **single-user**, `users`/`owner_id` are stubs), vector retrieval (P2), a real task queue.

## Architectural invariants — easy to get wrong, must hold

These span multiple docs and were the hard-won conclusions of design review. Violating them breaks the product's core value (long-form consistency) or its provider-neutrality.

1. **Memory (the DB) is the single source of truth.** Agents communicate *only* through DB tables, never by calling each other directly (`ARCHITECTURE.md §5.4`).
2. **Agents declare a capability `tier`** (`writer`/`analyst`/`light`), never a concrete model. The gateway maps tier→provider+model per config. Never hardcode a provider or SDK in agent/orchestrator code (`ARCHITECTURE.md §3.3/§4`).
3. **The four reviewers (continuity/foreshadow/style/pace) are READ-ONLY.** No AI output is written silently. Every write goes through the **accept transaction** (HITL gate) after the author adjudicates (`§5.5`). Unresolved conflicts must block accept (`CONFLICT_UNRESOLVED`).
4. **Chapter digest is extracted from the FINAL accepted text, not the review-time draft.** The author may edit during adjudication; extracting from the draft would pollute the truth source (`§5.4/§5.5/§6.1`).
5. **In-flight truth-source boundary**: `chapters` (text) and `chapter_reviews` (review results) are authoritative; the LangGraph checkpoint holds only control-flow position + a pending-decision handle. On resume, re-read domain tables (`§5.2`).
6. **Memory injection is deterministic selection** (explicit-named + main characters + recent N chapters), **not** vector search. Be aware of the coverage-gap mitigations (foreshadow-window entities, name-match FTS, author pin) in `§3.4`.
7. **`章 = f(outline, selected state, fingerprint)`** — writing a chapter is a pure function; all state mutation is isolated to the accept transaction.
8. **Naming contract**: backend is Python/Pydantic → **snake_case** everywhere (request/response fields, schemas). The frontend consumes these via OpenAPI codegen.
9. **Prompt caching** = stable prefix (`cache_control` breakpoint): stable core (world hard rules + fingerprint) in `system` before the breakpoint, volatile state (latest_state + outline) after it. Never put timestamps/UUIDs in the cached prefix.

## Development practices (开发模式) — non-negotiable

### TDD — test-first, always
- RED → GREEN → REFACTOR. Write the failing test **before** the implementation. Target ≥80% coverage (global rule).
- **Mock the LLM in every unit/integration test** — inject a fake gateway/provider returning fixtures. Tests must be deterministic, fast, cost-free; **never hit a real LLM API in tests/CI**.
- Scope: **Unit** (one node/function/Repository, mocked IO) → **Integration** (FastAPI endpoint + DB + mocked gateway; LangGraph flow paths) → **E2E** (write→review→accept with mock gateway).
- Run a single test: `pytest path/to/test.py::test_name -q` (backend) · `vitest run -t "name"` (frontend).

### Python
- **async-first**: FastAPI async endpoints, SQLAlchemy 2.0 async, async gateway calls. Never block the event loop (no sync DB/HTTP in async paths).
- Full type hints; `mypy` + `ruff` (lint+format) clean before commit. snake_case everywhere.
- Pydantic at every boundary (request/response, structured LLM output, config).
- Depend on Repository/gateway **interfaces**, not concrete impls, so tests inject fakes (ARCHITECTURE §3.5). Small focused functions/files; immutable updates (return new objects, per global coding-style rule).

### LangGraph (orchestrator)
- Nodes are **small deterministic units**; keep LLM nondeterminism behind the gateway so node logic is testable. Test nodes by **calling the node function directly** (no graph runtime) + test flow paths with a **mocked gateway**.
- Put `interrupt()` (the accept HITL pause) at the **start** of the node — don't duplicate logic or mutate state before interrupting.
- Use the **Postgres checkpointer** (durable/resumable across the write→accept request gap). Run `checkpointer.setup()` in **migrations/CI**, not in app runtime.
- Transient-failure retries (backoff/tenacity) live in the **gateway** (§4.5); a node surfaces a clean failure, never loops. On resume, re-read `chapters`/`chapter_reviews` from DB (checkpoint = control-flow only).

### Frontend
- TS strict; **use the OpenAPI-generated client** — never hand-write API types (regenerate on backend schema change).
- Server Components for read views; Client Components for editor/streaming/interaction.
- SSE: handle every event type in the stream contract (ARCHITECTURE §7 / `memory/contracts.md` — the authoritative event list); "stop" = abort the connection; reconnect resumes from the saved draft.
- Optimistic updates with rollback + toast on failure; respect `prefers-reduced-motion`; meet the a11y bar in `UX_SPEC.md §10`.

### Logging (埋好 log，便于查错) — wire from day one
Aligns `ARCHITECTURE.md §9.3`. This is how every failure gets diagnosed.
- **Structured logs** (`structlog`; JSON outside dev). Never `print`.
- **Correlation id per request**: generate/propagate `request_id`; one "write a chapter" = one trace spanning assemble→write→4 reviews→accept. Put `request_id` on every log line **and in the error response envelope** so a user-reported error is greppable end-to-end.
- **Log every LLM call** with the full call-metadata field set — feeds the cost ledger; the authoritative field list lives in `ARCHITECTURE §4.8` (don't duplicate it here).
- **Log graph transitions** (node enter/exit, interrupt, resume) tagged with `project_id`/`chapter_no`/`request_id`.
- **Errors** logged with full context at the boundary, mapped to the error envelope (`ARCHITECTURE §7.1` is the source of truth for envelope shape + error codes). Frontend logs client errors with the same `request_id`.
- **Redact**: never log API keys; truncate/hash full prompts and manuscript text (log lengths/hashes, not the novel).

### Definition of Done (每次编码完成的硬门禁) — 不通过不算完成

每一次代码改动（无论多小）在声明「完成」前，**必须**按顺序全部通过以下门禁。任一项失败就回去修，不得跳过、不得只跑部分、不得仅靠肉眼判断。

1. **TDD 已遵守** — 先写失败测试（RED）→ 实现到通过（GREEN）→ 重构（REFACTOR）。新增/修改的逻辑都有对应测试；改 bug 先写能复现的失败测试。见上文 §TDD。
2. **测试覆盖率 ≥ 80%（已工具化 + CI 强制）** — 改动涉及的模块覆盖率不得低于 80%；新代码不许拉低整体覆盖率。跑命令核对真实数字，不要估：
   - 后端：`uv run pytest -q --cov --cov-report=term-missing --cov-fail-under=80`（低于 80% 直接非零退出。当前基线 ~88%）。配置在根 `pyproject.toml` 的 `[tool.coverage.*]`。
   - 前端：`cd apps/web && pnpm test:coverage`（`vitest.config.ts` 设 lines/functions/branches/statements 阈值 80。当前基线 ~95%）。
   - **前端覆盖率范围 = `lib/**`（纯逻辑 + React hooks）**：hooks(`use*.ts`)用 jsdom + `@testing-library/react` 的 `renderHook` 单测（测试文件首行 `// @vitest-environment jsdom`，mock `@/lib/api/client` + `@/components/Toast`，参见 `lib/generation/useWorldGen.test.ts`）。组件(.tsx)仍由 E2E/人工验收覆盖，不在 vitest 范围（写新 hook 时必须同步补 `renderHook` 测试）。
3. **跑完三类自动化测试**（改动触及的那侧必跑，跨栈改动两侧都跑）：
   - **后端 Unit + Integration**：`uv run pytest -q`（需要 pg 的集成测试先 `docker compose up -d pg`）。
   - **前端自动化测试**：`cd apps/web && pnpm test`（vitest）。
   - **E2E**（动到 write→review→accept 主链路时）：跑相关 E2E 流程（mock gateway）。
   - LLM 一律 mock，**绝不在测试/CI 命中真实 LLAPI**（见上文 §TDD）。
4. **全部门禁绿**（与 §Toolchain、CI 一致）：
   - 后端：`uv run ruff check .` · `uv run ruff format .` · `uv run mypy packages apps` · `uv run pytest -q` · 动过模型时 `uv run alembic check` 无漂移。
   - 前端：`cd apps/web && pnpm lint` · `pnpm typecheck` · `pnpm test` · `pnpm build`；动过后端 schema 先 `pnpm gen:api` 再校验。
5. **符合架构与编程规范** — 不违反上文 §Architectural invariants、§Locked tech stack、§Python/§Frontend/§LangGraph 实践，以及全局编码风格（不可变更新、KISS/DRY/YAGNI、文件 <800 行、函数 <50 行、显式错误处理、边界校验、无硬编码密钥）。安全敏感改动（auth/输入处理/DB 查询/外部 API/加密）按 §code-review 触发安全自查。
6. **提交（每次完成都 commit）** — 见下文 §Commit discipline。
7. **回写状态/文档** — 按 §Per-task workflow 更新 `PROGRESS.md`；若实现暴露 spec 缺口，回写对应 spec（见 §Conventions）。

> 报告要诚实：测试失败就如实说明并贴输出；跳过了哪一步要讲明；只有真正跑过且全绿，才说「完成」。

### Commit discipline (提交纪律) — 每次完成即提交

- **每次完成一个可工作的改动就 commit**，小步提交、一次一个聚焦改动；不要把多个无关改动堆进一个提交。
- **提交前** Definition of Done 的门禁（1–5）必须已全绿——不提交跑不过测试或 lint/类型不干净的代码。
- **提交信息格式**：遵循 Conventional Commits —— `<type>: <description>`（type ∈ feat/fix/refactor/docs/test/chore/perf/ci），中文描述与本仓库现有提交风格一致；需要时附 body 说明「为什么」。
- **提交信息禁止任何署名/归属尾注** — **不得包含 `Co-Authored-By:` 或任何 `Generated with` / AI 署名行**（全局已在 `~/.claude/settings.json` 关闭 attribution；此处再次明确，覆盖任何默认加尾注的行为）。
- **分支**：不在 `main` 上直接提交；按 §Git Workflow 在特性分支工作。`develop` 为当前集成分支。
- **push / 开 PR 仅在用户明确要求时**进行；PR body 同样不加 AI 署名。

## Toolchain (Phase 0 已落地)

工具：**uv**(Python workspace，4 members: `apps/api` + `packages/{shared,config,db}`) + **pnpm**(前端，`apps/web`)。Python 3.12+，Node 22。

- **环境变量**：`uv` 装到 `~/.local/bin`，命令前 `export PATH="$HOME/.local/bin:$PATH"`。
- **依赖安装**：`uv sync`（后端，仓库根）；`pnpm install`（前端，`cd apps/web`）。
- **本地起服务（宿主端口 +1000 防冲突：web 4000 / api 9000 / pg 6432；容器内仍 3000/8000/5432）**：`docker compose up`（pg + api + web，端口已 +1000）。仅起库：`docker compose up -d pg`（宿主 6432）。裸跑：API `uv run uvicorn ww_api.main:app --port 9000 --reload`（读根 `.env`→pg 6432 / CORS 放行 4000）；前端 `cd apps/web && pnpm dev`（`next dev -p 4000`，读 `apps/web/.env.local`→`NEXT_PUBLIC_API_BASE=http://localhost:9000`）。`.env`/`.env.local` 为本机文件（gitignored）。
- **迁移**：`uv run alembic upgrade head`；改模型后 `uv run alembic revision --autogenerate -m "..."`；漂移校验 `uv run alembic check`（需 pg 在跑）。
- **后端门禁**：`uv run ruff check .` · `uv run ruff format .` · `uv run mypy packages apps` · `uv run pytest -q`。覆盖率门禁：`uv run pytest -q --cov --cov-report=term-missing --cov-fail-under=80`。单测：`uv run pytest path::test -q`（单测不带 `--cov`，避免 fail-under 误报）。
- **前端门禁**（`cd apps/web`）：`pnpm lint` · `pnpm typecheck` · `pnpm test`（vitest）· `pnpm build`。覆盖率门禁：`pnpm test:coverage`（阈值 80%，范围 `lib/**`）。
- **重生成 TS 客户端**：改后端 schema 后 `cd apps/web && pnpm gen:api`（离线：`uv run python -m ww_api.export_openapi` → `openapi-typescript` 生成 `lib/api/schema.d.ts`）。
- **pnpm 配置**：在 `apps/web/pnpm-workspace.yaml`（pnpm 11 不再读 package.json/.npmrc）——含 `onlyBuiltDependencies` 白名单 + `verifyDepsBeforeRun: false`（见 `memory/gotchas.md`）。
- **CI**：`.github/workflows/ci.yml`（backend job 带 pg service 跑 ruff/mypy/alembic/pytest；frontend job 跑 gen:api/lint/typecheck/test）。

## Conventions specific to this repo

- All four specs are kept mutually consistent. If implementation reveals a spec gap, update the spec(s) too — don't let code and spec drift. (The specs already track this discipline; see ARCHITECTURE's "待回写规格的发现" pattern.)
- Docs are in Chinese; keep new docs/comments consistent with surrounding language.

## Multi-agent collaboration (多 Agent 协同)

DEV_PLAN tasks are tagged with discipline skills (`@backend`/`@frontend`/`@db`/`@llm`/`@devops`/`@qa`) so they can run in parallel. Coordination is **file-based** through two artifacts. **Read both before starting any task.**

- **`PROGRESS.md`** — the single shared task ledger + chronological log. Claim, sequence, and report here.
- **`memory/`** — durable shared knowledge across sessions/agents:
  - `memory/contracts.md` — the decoupling seams (OpenAPI endpoints, Pydantic/TS schemas, gateway/orchestrator interfaces). **Contract-first**: the owning agent registers a contract here and marks it `稳定` *before* dependent agents start.
  - `memory/decisions.md` — implementation decisions not covered by the specs, with rationale (append-only, one decision per entry).
  - `memory/gotchas.md` — pitfalls/conventions discovered while building, so others don't repeat them (append-only).

### Directory ownership (sole-writer; others read-only)

Edit **only** files inside your skill's directories. This eliminates write conflicts. To change a file outside your scope, write a request entry in `PROGRESS.md` for the owner instead of editing it.

| Skill | Owns (sole writer) |
|---|---|
| `@db` | `packages/db/` |
| `@llm` | `packages/llm_gateway/`, `packages/agents/`, `packages/core/orchestrator/` |
| `@backend` | `apps/api/`, `packages/core/{memory,domain}/`, `packages/skills/`, `packages/shared/`, `packages/config/` |
| `@frontend` | `apps/web/` |
| `@devops` | repo-root config (`docker-compose*`, CI, `pyproject.toml`, `package.json`, Alembic config) |
| `@qa` | `tests/` (integration/E2E). Unit tests live with the owning module and are written by that module's owner. |

`packages/shared/` (Pydantic schemas) and `apps/api` OpenAPI are **contracts** — changing them requires a `memory/contracts.md` entry and a `PROGRESS.md` note so dependents re-generate the TS client / re-sync.

### Per-task workflow (every agent, every task)

1. **Read** `PROGRESS.md` + relevant `memory/` files.
2. **Check dependencies** — if an upstream task isn't `✅`, don't start; pick another or mark `⛔ blocked`.
3. **Claim** — set the task to `🔵 in-progress @<skill> <date>` in `PROGRESS.md`. If already `🔵` by someone else, skip it.
4. **Contract-first** — if your task defines a contract, register it in `memory/contracts.md` and mark `稳定` before dependents proceed. If you consume one, build against the registered contract, not assumptions.
5. **Work only in your owned directories**, one file at a time.
6. **On completion** — set `✅`, append a one-line entry to `PROGRESS.md`'s log (what changed + which contracts/files affected), and record any decision/gotcha in `memory/`.
7. **Honor the architectural invariants above** — they are not negotiable for speed.

### Memory hygiene

Append, don't rewrite history. One fact per entry. Don't duplicate what the specs or git already record — capture only what's non-obvious and useful to a sibling agent. Delete entries proven wrong.
