Files
writer-work-flow/CLAUDE.md

181 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Repository status
**Phase 05 (M1M5) 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 M1M5 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 05, 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 的门禁15必须已全绿——不提交跑不过测试或 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 workspace4 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/pytestfrontend 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.