Files
mini-openclaw-v2/scripts/test_improvement_panel.cjs
T
qiaoqichaoandClaude Code f775c6cbe4 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>
2026-10-01 02:21:11 +08:00

100 lines
6.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 改进对照与发布 界面源码守卫(纠偏第四批 · BR-51 / AC-56 / AC-57)。
*
* 这是**源码断言,不是浏览器端到端测试**(docs/v2/06-validation.md §2)。
* 它专防审计确认过的偏差模式:「函数在、功能不在」——面板看着有按钮,
* 实际没接真实端点,或者把版本号从界面自报上去冒充发布。
*/
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const root = path.resolve(__dirname, "..");
const read = (file) => fs.readFileSync(path.join(root, file), "utf8");
const exists = (file) => fs.existsSync(path.join(root, file));
const panel = read("src/components/xingbang/ImprovementPanel.tsx");
const page = read("src/app/trajectory-center/page.tsx");
const trajectory = read("src/components/xingbang/TrajectoryPanel.tsx");
const prescreenApi = read("backend/api/prescreen.py");
// ---------------------------------------------------------------- 1. 入口与挂载
assert.ok(exists("src/components/xingbang/ImprovementPanel.tsx"), "应有改进对照面板组件");
assert.match(page, /import ImprovementPanel from "@\/components\/xingbang\/ImprovementPanel"/,
"轨迹复盘路由应复用改进面板组件,而不是在路由里再写一份");
assert.match(page, /<ImprovementPanel/, "轨迹复盘路由必须真的挂载改进面板");
assert.match(page, /<TrajectoryPanel/, "轨迹复盘路由应仍挂载轨迹面板");
// ---------------------------------------------------------------- 2. 不新增第四个入口(AC-53/BR-50)
const nav = read("src/components/xingbang/AgentNav.tsx");
assert.ok(!nav.includes("/improvement"), "改进模块必须并入轨迹复盘,不得新增导航入口");
for (const banned of ["improvement", "improvements"]) {
assert.ok(!exists(`src/app/${banned}`), `不得新建第四个模块路由 src/app/${banned}`);
}
// ---------------------------------------------------------------- 3. 端点真的接上了
for (const call of [
/`\$\{PRESCREEN\}\$\{path\}\/improvements`/, // 项目级列表与提出候选
/`\$\{PRESCREEN\}\/improvements\/\$\{encodeURIComponent\(item\.id\)\}\/transitions`/,
/knowledge-candidates/, // 选草稿 + 草稿隔离评测
/review-outcomes/, // 偏差来源:会议结果
]) {
assert.match(panel, call, `改进面板必须真的调用既有端点:${call}`);
}
assert.match(panel, /action: "evaluate"|action,\s*\.\.\.extra/, "必须能提交对照评测");
assert.match(panel, /action: "release"|transition\(item, "release"\)/, "必须提供发布动作");
// ---------------------------------------------------------------- 4. 版本号不得由界面提交(BR-51 的核心)
assert.ok(!/released_ref\s*:/.test(panel),
"界面不得提交 released_ref:发布版本必须由服务端从真实发布结果产生");
assert.ok(!/released_ref\s*:/.test(trajectory), "会议结果录入不得提交 released_ref");
// 展示时只能读服务端返回的引用
assert.match(panel, /item\.released_ref/, "发布结果应展示服务端返回的版本引用");
// ---------------------------------------------------------------- 5. 不提供自动上线
const buttonTexts = [...panel.matchAll(/<button[^>]*>([\s\S]*?)<\/button>/g)].map((m) => m[1]);
assert.ok(!buttonTexts.some((text) => /自动上线|一键|自动发布/.test(text)),
"不得提供自动上线/一键发布按钮");
assert.match(panel, /没有"自动上线"|不提供自动上线/, "界面必须写明不提供自动上线");
assert.ok(!/enqueue|\/runs`/.test(panel), "发布后不得自动发起运行");
// ---------------------------------------------------------------- 6. 不自批
assert.match(panel, /proposed_by !== identity\?\.id/, "提出者不得看到自己的通过审核按钮");
assert.match(panel, /独立审核|不能由提出者|审核需要独立/, "界面必须说明审核需要独立的人");
assert.match(panel, /release_blockers|blockersOf|未完成门禁/, "必须把未完成门禁展示出来(E-24)");
// ---------------------------------------------------------------- 7. 两道评测门必须分开
assert.match(panel, /隔离评测/, "必须有草稿的隔离评测入口(发布门禁之一)");
assert.match(panel, /旧\/新对照/, "必须有候选的旧/新对照入口(另一道门)");
// 草稿评测必须精确到 `组件id@版本`:只传组件 id 会评测到"当前最新草稿",
// 于是出现"评测的是 A、发布的是 B"。
assert.match(panel, /proposed_revision_ref\.id \+ "@" \+ item\.proposed_revision_ref\.version/,
"草稿隔离评测必须精确定位到候选声明的版本");
assert.match(panel, /泄漏/, "泄漏样本必须按失败显示,不得渲染成评测完成");
// ---------------------------------------------------------------- 8. AC-56:会议结果录入
assert.match(trajectory, /review-outcomes/, "轨迹面板应消费会议结果端点");
assert.match(trajectory, /"POST"/, "轨迹面板必须能追加会议结果");
assert.match(trajectory, /raw_code/, "录入必须围绕原始结果码,不得自行推断通过或否决");
assert.match(trajectory, /supersedes_id/, "更正只能追加并指向旧记录");
assert.match(trajectory, /不计入分母/, "未匹配的结果必须单列且不计入分母");
// ---------------------------------------------------------------- 9. 后端真的接上了(交叉断言)
assert.match(prescreenApi, /publisher=_knowledge_publisher\(/, "端点必须注入真实 publisher");
assert.match(prescreenApi, /knowledge_cards\.publish\(db, draft\["id"\], actor, preview\)/,
"publisher 必须调用真实知识发布入口");
assert.ok(!/released_ref=payload\.released_ref/.test(prescreenApi),
"端点不得再把客户端提交的 released_ref 透传给服务层");
assert.match(prescreenApi, /不接受客户端提交的 released_ref/, "必须显式拒绝客户端自报版本号");
assert.match(read("backend/services/improvements.py"), /released_ref = publisher\(body\)/,
"服务层发布必须委托注入的 publisher");
// ---------------------------------------------------------------- 10. 不新增样式表 / 既有禁令
assert.ok(!/^import .*\.css";/m.test(panel), "改进面板应复用 workspace.css,不得新增样式表");
for (const [name, source] of [["improvement", panel], ["trajectory", trajectory]]) {
assert.ok(!/0\.(42|58|51|64)\b/.test(source), `${name} 不得硬编码演示概率值`);
assert.ok(!/"(00|10|01|11)"\s*:/.test(source), `${name} 不得复制演示情景表`);
}
console.log("improvement-panel-ui: PASS");