feat(web): AI 立项方案生成+预填(种子门控 + 按字段接受)

向导阶段(project 未建)一键推演结构化书级蓝图预填立项向导。

- 新 SPEC project_plan_spec(analyst 档,纯预览 reads/writes=())+ schema
  ProjectPlanResult(书名候选 + 时空背景/叙事结构/故事核心/结局设计/基调,
  全字段默认值守解析韧性)+ 注册 SCHEMA_CATALOG;计数三处 21→22 + regen golden。
- 端点 POST /skills/project-plan/generate(不带 project 前缀,绕开 toolbox 404):
  请求体序列化向导草稿为 project_context,复用 build_brief_context + run_generator;
  种子门控(缺 genre/logline→422);请求 schema 加 max_length 上界。
- run_generator/_build_request 的 project_id 放宽 uuid.UUID|None(向导无 project)。
- 前端 wizard.ts 加 mapPlanResultToWizardForm(错配字段折进 premise 不静默丢)+
  applyPlanPatch(仅回填空字段、保护已编辑)+ useProjectPlan hook + PlanAssistant。
This commit is contained in:
Yaojia Wang
2026-07-06 16:26:08 +02:00
parent 821ace5989
commit 02d19019f6
22 changed files with 1187 additions and 9 deletions

View File

@@ -346,6 +346,8 @@
## 契约变更日志append-only
> 格式:`- [date] @skill 改 Cx<改了什么> → 影响 <依赖方/任务>`
- [2026-07-06] @llm/@backend/@frontend C3 新端点(⑤ AI 立项方案生成**`POST /skills/project-plan/generate`****不带 project 前缀**——立项前 project 未建绕开 toolbox `project_repo.get→404`)← `ProjectPlanGenerateRequest{genre?,logline?,title?,premise?,theme?,structure?,tone?,ending_type?,narrative_pov?,selling_points[],brief?}`全字段 `max_length` 上界 CR-H9`brief` `str|None` codegen 渲染为可选)→ `ProjectPlanView{title_candidates[str], setting, narrative_structure, story_core, ending_design, tone}`全字段默认值守解析韧性)。**种子门控** genre loglinestrip 后空)→ **422 VALIDATION**空向导不出方案 slop无凭据503 SPEC `project_plan_spec``tier="analyst"` 不写 model`reads=()`/`writes=()` 纯预览`prompts/project-plan.md`+ schema `ProjectPlanResult``ww_agents.schemas`+ 注册 `SCHEMA_CATALOG["project-plan"]`**计数三处 +12122**`specs.py` assert`test_prompt_loader.py` len 断言`schema_catalog.py` docstring**regen `prompt_hashes.json`**仅加 project-plan 一条)。端点复用 `build_brief_context`+`run_generator`**`run_generator`/`_build_request` `project_id` 放宽为 `uuid.UUID|None`**——向导阶段无 project`usage_ledger.project_id` 本就 nullable新网关缝 `get_project_plan_gateway`analyst `project_deps.py`)。**零迁移**纯预览不写业务表仅落 usage ledger)。前端 `wizard.ts` `mapPlanResultToWizardForm`书名候选title叙事结构structure基调tone时空背景/结局设计无独立向导字段折进 premise 带标签**不静默丢**结局取向/叙事视角预设枚举不臆造+ `applyPlanPatch`**仅回填空字段保护已编辑**+ `canGeneratePlan`/`toPlanSeedRequest` + `useProjectPlan` hookrenderHook 测试+ `ProjectWizard.tsx` 2 `PlanAssistant`生成预览按字段回填)。→ 影响 @frontend `pnpm gen:api`)。依赖 ④(tone/ending_type/narrative_pov 已入 project 契约与向导)。
- [2026-07-06] @db/@backend/@frontend C2+C3(④ 立项基调/结局/视角`projects` 表加 3 `tone`/`ending_type`/`narrative_pov`**Text nullable CHECK** genre/structure迁移 `e5f6a7b8c9d0`nullable backfill)。`ProjectCreateRequest`+`ProjectResponse``schemas/projects.py`各加 3 字段 `str|None`** Literal**Literal 只留 Verdictdomain `ProjectCreate`/`ProjectView`/`_to_view`/`SqlProjectRepo.create` + `FakeProjectRepo` 同步透传完整 stable_core 链路`ProjectSpecView``repositories.py`frozen 3 字段 + `SqlProjectSpecRepo.spec` 查询构造带上 + `_build_spec_section``assemble.py`渲染进作品蓝本**书级常量缓存前缀字节稳定守 #9**绝不入 volatile)。前端 `wizard.ts` `WizardForm`/`emptyWizardForm`/`toCreateRequest` + 预设数组 `TONES`/`ENDING_TYPES`/`NARRATIVE_POVS`仿 GENRES/STRUCTURES+ `ProjectWizard.tsx` 3 步控件toneSelectpov/endingSegmentedControl+ 确认页回显。**codegen**3 字段皆 `= None` 无默认工厂 openapi-typescript 渲染为 `?: string | null` 可选。→ 影响 @frontend `pnpm gen:api`未来 AI 立项方案回填这 3 字段
- [2026-07-06] @llm/@backend C3(⑧ appearance/motive 穿线`CharacterCardView``schemas/generation.py`+ `ww_agents.CharacterCard` 各加 `appearance:str=""` + `motive:str=""`**均给默认值守解析韧性**DB `characters.appearance/motive` 早已在初始迁移此前一直 NULL——**无新迁移**)。写侧 `CharacterWriteRepo.create`/`SqlCharacterWriteRepo`create+**update backfill 分支**/`CharacterWriteFields` 同步加两列`routers/generation.py` `_card_to_view`/`_view_to_card`/`_existing_characters` 双向形变带上两字段同批**重切 motive/traits/arc 口径**动机唯一落 `motive``traits[]` 只留核心/表层/阴影三层`arc` 引用 motive `character-gen.md` regen `prompt_hashes.json` 金标准)。**codegen 注意**因带 `default`openapi-typescript 把两字段渲染为**必填**响应恒有值前端 `CharacterCardView` 字面量构造点须显式给两字段。→ 影响 @frontend `pnpm gen:api` + `CharacterCardItem` 展示/编辑两字段未来 人物塑造审查以 motive/appearance 为客观锚点