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:
@@ -222,13 +222,32 @@ class ImprovementApiTests(unittest.TestCase):
|
||||
candidate = created.json()
|
||||
self.assertEqual(candidate["status"], "draft")
|
||||
|
||||
# release 不再接受调用方自报的版本号(BR-51 / AC-57);未过门禁在此先命中
|
||||
blocked = self.client.post(
|
||||
f"{self.base}/improvements/{candidate['id']}/transitions",
|
||||
json={"action": "release",
|
||||
"released_ref": {"id": "card-1", "version": "2.0.0"}}, headers=HEADERS)
|
||||
json={"action": "release"}, headers=HEADERS)
|
||||
self.assertEqual(blocked.status_code, 409)
|
||||
self.assertEqual(blocked.json()["error"]["code"], "IMPROVEMENT_NOT_VALIDATED")
|
||||
|
||||
def test_client_supplied_released_ref_is_rejected(self):
|
||||
"""发布版本必须由服务端从真实发布结果产生,不接受调用方提交。"""
|
||||
created = self.client.post(f"{self.base}/projects/{PROJECT}/improvements",
|
||||
json={"target_ref": {"id": "card-1", "version": "1.0.0"},
|
||||
"proposed_revision_ref": {"id": "card-1", "version": "2.0.0"},
|
||||
"change_summary": "扩适用范围",
|
||||
"source_run_ids": ["run-1"]}, headers=HEADERS).json()
|
||||
response = self.client.post(
|
||||
f"{self.base}/improvements/{created['id']}/transitions",
|
||||
json={"action": "release",
|
||||
"released_ref": {"id": "card-1", "version": "2.0.0"}}, headers=HEADERS)
|
||||
self.assertEqual(response.status_code, 422, response.text)
|
||||
self.assertEqual(response.json()["error"]["code"], "INVALID_REQUEST")
|
||||
# 自报的版本号不得让候选变成"已发布"
|
||||
stored = self.client.get(f"{self.base}/projects/{PROJECT}/improvements",
|
||||
headers=HEADERS).json()["items"][0]
|
||||
self.assertEqual(stored["status"], "draft")
|
||||
self.assertIsNone(stored["released_ref"])
|
||||
|
||||
def test_leakage_is_reported_as_e11(self):
|
||||
created = self.client.post(f"{self.base}/projects/{PROJECT}/improvements",
|
||||
json={"target_ref": {"id": "card-1", "version": "1.0.0"},
|
||||
|
||||
Reference in New Issue
Block a user