Files
writer-work-flow/CLAUDE.md
Yaojia Wang d3dc620a71 feat: Phase 0 — monorepo 骨架 + 全表迁移 + FastAPI/Next 骨架 + CI
- uv(workspace) + pnpm monorepo;docker-compose(pg+api+web)
- SQLAlchemy 16 MVP 表 + Alembic 初版迁移(无漂移,users stub)
- FastAPI 骨架:统一错误信封(带 request_id) + structlog + /jobs/:id + OpenAPI
- Next.js 骨架:纸感主题 token + OpenAPI→TS 客户端代码生成(gen:api)
- CI(ruff/mypy/pytest + pg service + alembic 漂移校验)
- 四份设计规格(PRODUCT/UX/ARCHITECTURE/DEV_PLAN) + CLAUDE.md
2026-06-18 11:38:28 +02:00

14 KiB
Raw Blame History

CLAUDE.md

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

Repository status

This is a spec-only repository at the pre-implementation stage. There is no application code yet — only four design documents. The next step is Phase 0 / T0.1 in DEV_PLAN.md (scaffold the monorepo). Do not assume any build/test tooling exists until you create it.

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).

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)。
  • 本地起服务docker compose uppg + api + web。仅起库docker compose up -d pg。裸跑 APIuv run uvicorn ww_api.main:app --reload
  • 迁移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 path::test -q
  • 前端门禁cd apps/webpnpm lint · pnpm typecheck · pnpm testvitest· pnpm build
  • 重生成 TS 客户端:改后端 schema 后 cd apps/web && pnpm gen:api(离线:uv run python -m ww_api.export_openapiopenapi-typescript 生成 lib/api/schema.d.ts)。
  • pnpm 配置:在 apps/web/pnpm-workspace.yamlpnpm 11 不再读 package.json/.npmrc——含 onlyBuiltDependencies 白名单 + verifyDepsBeforeRun: false(见 memory/gotchas.md)。
  • CI.github/workflows/ci.ymlbackend 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.