feat(v2): 纠偏第四批——会议结果录入与改进发布接真实知识发布入口
- api/prescreen.py:transition_improvement 注入 _knowledge_publisher, release 委托 knowledge_cards.publish → component_registry.release; 按 proposed_revision_ref 以「组件id@版本」精确解析草稿(不取"最新草稿", 否则会出现"审核的是 A、发布的是 B");客户端自报 released_ref 一律 422; 非 knowledge 目标 409 诚实拒绝;发布与候选状态同一事务,失败保留旧版。 - TrajectoryPanel:新增会议结果录入(追加式更正走 supersedes_id; 未匹配单列且不计入分母)。 - ImprovementPanel(新增):偏差→候选→两道评测门→独立审核→发布; 请求体不含版本号,无自动上线,不自批。 - 界面并入「轨迹复盘」,不新增第四个导航入口(AC-53)。 - 守卫:新增 scripts/test_improvement_panel.cjs(verify.py 13→14 项); test_module_shell_ui.cjs 的「只读」断言改为分别锁死「回放只读」与「结果只可追加」。 - 测试:新增 backend/test_improvement_release.py(AC-57 端到端 + 三条反例); test_improvements.py 补「自报版本号被拒」。 - 文档:17 第四批交付记录、15 §7 交接、03 §9.4 发布事务边界。 验证:scripts/verify.py 14/14 PASS(backend-unit 572 项),证据 test-evidence/20261001-013517-3e15bda/;scripts/xingbang_batch4_smoke.py 对真实服务器实跑通过(发布指针实测移到新版本)。 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -271,4 +271,88 @@ XINGBANG_LOCAL_MODEL_KEY=<你的 DeepSeek API Key>
|
||||
|
||||
## 第四批:接通评审反馈与改进发布
|
||||
|
||||
状态:**未开始**。
|
||||
状态:**部分完成**。会议结果可从页面录入并与材料版本对照;改进候选走**真实知识发布入口**,
|
||||
发布后新运行使用新版本、旧运行仍可回放——两条断言都由真实执行链(enqueue + worker)证明。
|
||||
**未闭合**:界面仍未经浏览器点击/截图验证(同 F11);会议结果是**模拟录入**,未经真实会议系统。
|
||||
|
||||
覆盖:AC-56、AC-57、BR-51。
|
||||
|
||||
### 页面入口
|
||||
|
||||
| 入口 | 位置 |
|
||||
|---|---|
|
||||
| 轨迹复盘(既有入口,**不新增第四个**) | 深色导航「轨迹复盘」→ `/trajectory-center` |
|
||||
| 会议结果录入与对照 | 同页 `TrajectoryPanel` 下半部「会议结果对照」→「追加会议结果」表单 |
|
||||
| 改进对照与发布 | 同页 `ImprovementPanel`「改进对照与发布」 |
|
||||
|
||||
置于轨迹复盘之内而非新开模块:14 §7 本就把「轨迹、会议记录与反向改进」写在同一节,
|
||||
AC-53 又要求恰好三个模块入口。`AgentNav.tsx` 未改动。
|
||||
|
||||
### 操作步骤(实现并按此核对)
|
||||
|
||||
1. **录入会议结果**:选项目 → `TrajectoryPanel` 底部填原始结果码、会议编号、可从
|
||||
`project.runs[].input_snapshot_id` 选「对应的材料版本」→ 保存。
|
||||
2. **更正**:在「更正哪条记录」里选旧记录 → 保存为**追加**并带 `supersedes_id`,历史不被覆盖。
|
||||
3. **提改进候选**:`ImprovementPanel` ① 选来源运行/会议结果(必填其一)、改进目标、
|
||||
要发布的草稿版本、变更说明 → 「提出改进候选」。
|
||||
4. **两道门**:② 「对草稿做隔离评测」(发布门禁)与「提交旧/新对照」(候选门禁)是两件事,缺一不可。
|
||||
5. **审核与发布**:提交审核 → 由**另一位**身份「通过审核」→「审核发布」。
|
||||
发布请求体**只有 action**,界面不提交任何版本号。
|
||||
|
||||
### 实际结果
|
||||
|
||||
- **发布是真的发布**(本批核心):端点把 `_knowledge_publisher` 注入 `improvements.transition`,
|
||||
publisher 用候选声明的 `proposed_revision_ref` **精确**定位草稿(`组件id@版本`,
|
||||
不按组件 id 猜"最新草稿",否则会出现"审核的是 A、发布的是 B"),再调
|
||||
`knowledge_cards.publish` → `component_registry.release`:自批拒绝、隔离评测门禁、
|
||||
依赖可解析三道既有保证原样生效,写入不可变 `component_version` 并**覆盖 `release` 指针**。
|
||||
- **不接受自报版本号**:release 请求带 `released_ref` 一律 422 `INVALID_REQUEST`,
|
||||
候选状态不前进、不产生任何组件版本(反例已测)。
|
||||
- **发布失败保留旧版**:未过隔离评测的草稿 → 409 `IMPROVEMENT_NOT_VALIDATED`,
|
||||
`release` 指针仍指旧版本,候选仍是 `approved`。候选状态与组件版本写在**同一个 `_db()`
|
||||
连接/事务**里,publisher 抛异常即整体回滚,不会出现"版本已发布、候选没标记"。
|
||||
- **非知识类目标诚实拒绝**:`orchestration` / `question_profile` 无真实发布入口 →
|
||||
409 并说明原因,不用"改状态"冒充发布。
|
||||
- **AC-57 端到端**(`test_improvement_release.py`,真实 enqueue + `DurableWorker.process_next()`):
|
||||
同一个已发布方案,发布前运行的 `component_versions` 含 `卡片@1.0.0`、
|
||||
`basis_ids=[卡片@1.0.0]`;经改进候选发布 `1.0.1` 后**再跑一次**得到 `卡片@1.0.1`;
|
||||
回放第一次运行仍是 `1.0.0`,其冻结版本未被改写(`replay.read_only=true`)。
|
||||
- **AC-56**:会议结果按 `matched` / `unmatched` 分组;未匹配的单列并显示「不计入分母」,
|
||||
与服务端 `denominator()` 一致(未送审、撤回、附条件、未知与未匹配都不进分母)。
|
||||
- **真实服务器冒烟**(不是单测的替代,脚本 `scripts/xingbang_batch4_smoke.py`):
|
||||
对运行中的 uvicorn 按界面同一串请求走通
|
||||
建卡 → 隔离评测 → 发布 → 结构化修订 → 录入会议结果 → 提候选 → 对照 → 换身份审核
|
||||
→ 发布(不带版本号);服务端返回 `released_ref=卡片@1.0.1`,且 `release` 指针实测指向 `1.0.1`。
|
||||
只用了脚本新建的组件 id,未触碰既有组件的发布指针。
|
||||
**副作用**:它在演示库(`backend/xingbang_data/project_assistant.sqlite3`)新建了若干
|
||||
「冒烟:回款集中依赖核查」卡片与 `SMOKE-4` 会议结果;重复运行会累积。
|
||||
这些是演示库里的可见脏数据,需要时可删——本记录如实列出,不做隐藏。
|
||||
|
||||
### 证据位置
|
||||
|
||||
| 内容 | 位置 |
|
||||
|---|---|
|
||||
| AC-57 端到端与四条反例(4 项) | `backend/test_improvement_release.py`(新增) |
|
||||
| 自报版本号被拒(API 层) | `backend/test_improvements.py` `test_client_supplied_released_ref_is_rejected` |
|
||||
| 界面源码守卫(新增) | `scripts/test_improvement_panel.cjs`(已登记 `scripts/verify.py`,共 **14** 项检查) |
|
||||
| 轨迹面板守卫(改:区分「回放只读」与「结果只可追加」) | `scripts/test_module_shell_ui.cjs` |
|
||||
| 全量验证 | `test-evidence/20261001-013517-3e15bda/`(`scripts/verify.py` **14/14 PASS**,含 `backend-unit` 全量 **572** 项) |
|
||||
| 真实服务器冒烟 | `scripts/xingbang_batch4_smoke.py`(对 127.0.0.1:8012 实跑,退出码 0) |
|
||||
|
||||
### 剩余缺口
|
||||
|
||||
- **界面仍未经浏览器验证(F11 未变)**:本批新增的两个表面只有「源码守卫 + 生产构建
|
||||
(`/trajectory-center` 3.87 → 8.86 kB)+ 真实服务器接口冒烟」三层证据,**没有浏览器点击或截图**;
|
||||
表单交互、按钮、权限隐藏这些路径没有端到端证明。
|
||||
- **会议结果是模拟录入**:AC-56 验的是"录入后与材料版本对照"的机制,不是真实会议系统对接;
|
||||
真实标签映射与客户身份权限仍待确认(M5/M6)。
|
||||
- **对照样本是人工录入的 JSON**,不是真实保留集;`evaluation.independent` 仍是**客户端声明**
|
||||
的布尔(既有设计)——界面不把它包装成"已证明独立",但服务端也确实无法自证。
|
||||
- **改进端点仍无角色门禁**:既有 `transition_knowledge_candidate` / `transition_review_plan` 对
|
||||
评测与发布有 `role()` 门禁,改进端点(propose/evaluate/approve/release)只做项目范围检查。
|
||||
本批给 release 接上真实发布后这个缺口的后果变重了,但加门禁会改变既有端点行为
|
||||
(现有 API 测试以 `manager` 身份调用),**未擅自扩大改动**,留待确认。安全上真正关键的
|
||||
"不自批"已由服务端强制(`must_not_self_approve` + `registry.release()` 的 `edited_by == actor`)。
|
||||
- **题库/编排无真实发布入口**:已按 409 诚实拒绝,非假接通;需要时另行设计。
|
||||
- **顺带发现、未修**:`backend/test_trajectory.py:173` 依赖 `recorded_at` 排序,两条记录落在
|
||||
同一时钟刻度时顺序会翻转(本次全量跑中出现过一次,重跑即通过)。属既有测试脆弱性,非本批引入。
|
||||
|
||||
Reference in New Issue
Block a user