feat(review): 人物塑造 advisory 审(第五审,仅建议不阻断验收)

写→审链新增第五审「人物塑造」(灵感③/D2):advisory 单维——仅建议、不阻断
验收(不进 conflicts、不碰 assert_conflicts_resolved、不入 REVIEW_RESERVED_NAMES);
只做人物塑造、不做氛围。

- SPEC characterization_spec(analyst,reads=("characters",),writes=())+
  prompts/characterization.md(只读、显式忽略文本长度与辞藻华丽度、引用逐字片段+置信度)
- schema CharacterizationReview{issues[...]}(全字段默认值守解析韧性)+ 注册 catalog
- 计数三处 22→23 + regen prompt_hashes.json(仅加一条)
- DB chapter_reviews 加 characterization JSONB nullable 列(迁移 f6a7b8c9d0e1)
- 接线:graph REVIEW_SPECS 追加 · chain ReviewRecordRepo · collect(extract/record) ·
  review_repo(Protocol/Sql/_to_view/ReviewView) · sse(event/factory/section) · normalize 自动纳入
- 契约 ReviewHistoryItem 加 characterization(gen:api 已重生成)
- 前端 sse/history/useReviewStream + CharacterizationPanel(只展示无裁决 UI、置信度降序+低置信折叠)+ ReviewReport 挂面板
- 测试 test_characterization_does_not_block_accept + 计数/golden + collect/normalize + 前端 reducer/normalize/parse

守不变量 #2/#3/#9;依赖 ⑧(motive/appearance 作客观锚点)。
This commit is contained in:
Yaojia Wang
2026-07-06 17:08:38 +02:00
parent 2a7a865e98
commit 72b3c81146
37 changed files with 1071 additions and 19 deletions

View File

@@ -346,6 +346,8 @@
## 契约变更日志append-only
> 格式:`- [date] @skill 改 Cx<改了什么> → 影响 <依赖方/任务>`
- [2026-07-06] @llm/@db/@backend/@frontend C3+C4(③ 人物塑造 advisory 新增第五审 **人物塑造**advisory 单维——仅建议、**不阻断验收**不进 conflicts不碰 `assert_conflicts_resolved`、**不入 `REVIEW_RESERVED_NAMES`**D2)。**DB**`chapter_reviews` 1 `characterization`**JSONB nullable**迁移 `f6a7b8c9d0e1`独立于恒 None health_scorenullable backfill)。**契约变更**`ReviewHistoryItem``schemas/projects.py`+ `ReviewView``domain/review_repo.py`末位默认 None 优雅降级各加 `characterization: dict|None``list_reviews` 路由透传 SPEC `characterization_spec``tier="analyst"` 不写 model`reads=("characters",)` 以人物卡 motive/appearance 为客观锚点`writes=()` 只读`prompts/characterization.md` **显式忽略文本长度与辞藻华丽度**引用逐字片段+置信度+ schema `CharacterizationReview{issues:[CharacterizationIssue{character,aspect,where,quote,diagnosis,suggestion,confidence}]}`全字段默认值守解析韧性+ 注册 `SCHEMA_CATALOG["characterization"]`**计数三处 +12223**`specs.py` assert`test_prompt_loader.py` len 断言`schema_catalog.py` docstring**regen `prompt_hashes.json`**仅加 characterization 一条)。接线链`graph.py REVIEW_SPECS` 末位追加并行扇出自动汇入 collect`chain/nodes.py ReviewRecordRepo.record` + `collect.py {CHARACTERIZATION 常量, extract_characterization, ReviewRecorder Protocol, collect_reviews}` + `review_repo.py {Protocol.record, SqlReviewRepo.record, _to_view}` 均加 `characterization` kwarg/字段`sse.py {EVENT_CHARACTERIZATION, characterization_event, _section_result_events elif}`normalize sorted 键自动纳入——字典序 characterization 排最前不打乱既有四审断言)。前端 `lib/review/sse.ts``CharacterizationIssue`/`CharacterizationReport`/`CharacterizationEvent` + KNOWN_EVENTS + `asCharacterizationIssues` 守卫 + reducer + state+ `lib/review/history.ts normalizeCharacterization` + `useReviewStream ReviewSeed` + `CharacterizationPanel.tsx`advisory 只展示、**无裁决 UI**、按置信度降序 + 低置信度折叠控注意力预算+ `ReviewReport.tsx` 挂面板 + seed 。**codegen**`characterization` `= None` openapi-typescript 渲染为可选。**测试**`test_characterization_does_not_block_accept` continuity 冲突 + advisory 问题 验收直通+ 计数/golden + collect 映射 + normalize 事件 + 前端 reducer/normalize/parse 纯函数。→ 影响 @frontend `pnpm gen:api`)。依赖 ⑧(motive/appearance 已入 CharacterCard 契约作客观锚点)。
- [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 字段