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:
@@ -111,13 +111,22 @@ const trajectory = read("src/components/xingbang/TrajectoryPanel.tsx");
|
||||
assert.match(trajectory, /failed/, "轨迹面板必须能显示失败步骤(失败保留)");
|
||||
assert.match(trajectory, /不填 0|未记录/, "成本缺失时必须留空,不得填 0");
|
||||
assert.ok(!/cost\s*[:=]\s*0\b/.test(trajectory), "不得把缺失成本写成 0");
|
||||
assert.match(trajectory, /只读/, "轨迹面板必须声明只读");
|
||||
// 纠偏第四批把"会议结果录入"并入了本面板,因此这里不能再只断言"整页只读"——
|
||||
// 要分别锁死两件事:**回放只读**(不改运行、不改历史结果)与**结果只可追加**(不覆盖历史)。
|
||||
assert.match(trajectory, /回放是只读的|只读回放/, "回放必须声明只读(第四批加的是追加,不是改写)");
|
||||
assert.match(trajectory, /supersedes_id/, "会议结果更正必须指向旧记录(supersedes_id)");
|
||||
assert.match(trajectory, /不覆盖|只\*\*追加\*\*/, "界面必须声明更正只追加、不覆盖历史");
|
||||
// 判据落在"是否渲染推理字段"上,而不是禁用词本身——否则解释规则的注释会把守卫自己绊倒。
|
||||
assert.ok(!/\.\s*(reasoning|reasoning_content|thoughts?|chain_of_thought)\b/i.test(trajectory),
|
||||
"不得渲染模型隐藏推理字段");
|
||||
// 服务端已声明轨迹不含内部推理,界面必须把它显示出来(而不是自己编一句)
|
||||
assert.match(trajectory, /trajectory\.note|note\}/, "应展示服务端对轨迹范围的说明");
|
||||
assert.match(trajectory, /review-outcomes|会议结果/, "轨迹面板应与会议结果对照");
|
||||
// AC-56:既读又写——只读列表不再是本面板的全部职责
|
||||
assert.match(trajectory, /review-outcomes/, "轨迹面板应消费会议结果端点");
|
||||
assert.match(trajectory, /"POST",\s*`\$\{PRESCREEN\}\$\{path\}\/review-outcomes`/,
|
||||
"会议结果必须真的能录入(POST 到 review-outcomes)");
|
||||
assert.match(trajectory, /raw_code/, "录入必须围绕原始结果码,不得自行推断通过或否决");
|
||||
assert.match(trajectory, /不计入分母/, "未匹配的结果必须标明不计入分母(单列)");
|
||||
// 预审模块必须真的挂载预审面板(否则"AI 预审入口"是空壳)
|
||||
assert.match(read("src/app/prescreen-workbench/page.tsx"), /<PrescreenPanel /, "AI 预审模块应挂载预审面板");
|
||||
|
||||
@@ -125,7 +134,9 @@ assert.match(read("src/app/prescreen-workbench/page.tsx"), /<PrescreenPanel /, "
|
||||
// 界面调用的端点必须在后端真的声明过——否则"入口可点"只是连到 404。
|
||||
const prescreenApi = read("backend/api/prescreen.py");
|
||||
for (const route of ['"/projects/{project_id}/runs/{run_id}/trajectory"',
|
||||
'"/projects/{project_id}/review-outcomes"']) {
|
||||
'"/projects/{project_id}/review-outcomes"',
|
||||
'"/projects/{project_id}/improvements"',
|
||||
'"/improvements/{candidate_id}/transitions"']) {
|
||||
assert.ok(prescreenApi.includes(route), `后端应声明路由 ${route}`);
|
||||
}
|
||||
assert.ok(read("backend/api/project_assistant.py").includes('"/projects/{project_id}/runs"'),
|
||||
|
||||
Reference in New Issue
Block a user