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:
@@ -2,6 +2,7 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import AgentNav from "@/components/xingbang/AgentNav";
|
||||
import TrajectoryPanel from "@/components/xingbang/TrajectoryPanel";
|
||||
import ImprovementPanel from "@/components/xingbang/ImprovementPanel";
|
||||
import {messageOf} from "@/lib/prescreen";
|
||||
import "../data-access/workspace.css";
|
||||
|
||||
@@ -9,6 +10,9 @@ import "../data-access/workspace.css";
|
||||
*
|
||||
* 原 `/trajectory` 是合并后的重定向桩,本页是**新路由**,不复用也不复活那个旧页
|
||||
* (AC-55 的"防重复页面")。只负责项目上下文与请求助手,回放逻辑在 TrajectoryPanel。
|
||||
*
|
||||
* 纠偏第四批把「会议记录」与「反向改进」并入本模块(14 §7 本就把三者写在一节),
|
||||
* 因此**不新增第四个导航入口**(AC-53 要求恰好三个)。
|
||||
*/
|
||||
export default function TrajectoryCenterPage() {
|
||||
const [identity, setIdentity] = useState<any>(null);
|
||||
@@ -26,9 +30,11 @@ export default function TrajectoryCenterPage() {
|
||||
...(role ? {"X-Xingbang-Demo-Role": role} : {})};
|
||||
}
|
||||
|
||||
async function request(path: string, body?: any) {
|
||||
// 必须支持 PATCH:改进候选的"自然语言修改适用范围"走的是既有候选端点的 PATCH。
|
||||
// 形参与 /experience-center 一致,两个模块不各写一份请求助手。
|
||||
async function send(method: string, path: string, body?: any) {
|
||||
const response = await fetch(`/api/risk-agent/project-assistant${path}`,
|
||||
{method: body === undefined ? "GET" : "POST", headers: headers(),
|
||||
{method, headers: headers(),
|
||||
body: body === undefined ? undefined : JSON.stringify(body)});
|
||||
const data = await response.json();
|
||||
if (!response.ok) throw Error(messageOf(data));
|
||||
@@ -40,7 +46,7 @@ export default function TrajectoryCenterPage() {
|
||||
try {
|
||||
const [context, list] = await Promise.all([
|
||||
fetch("/api/risk-agent/access/context", {headers: headers(false)}).then((r) => r.ok ? r.json() : null).catch(() => null),
|
||||
request("/projects"),
|
||||
send("GET", "/projects"),
|
||||
]);
|
||||
setIdentity(context);
|
||||
const items = list.items || list || [];
|
||||
@@ -48,7 +54,7 @@ export default function TrajectoryCenterPage() {
|
||||
const wanted = new URLSearchParams(location.search).get("project") || items[0]?.id || "";
|
||||
if (wanted) {
|
||||
setProjectId(wanted);
|
||||
setProject(await request(`/projects/${encodeURIComponent(wanted)}`));
|
||||
setProject(await send("GET", `/projects/${encodeURIComponent(wanted)}`));
|
||||
}
|
||||
} catch (e: any) {setError(e.message);} finally {setLoaded(true);}
|
||||
})();
|
||||
@@ -57,7 +63,7 @@ export default function TrajectoryCenterPage() {
|
||||
async function open(id: string) {
|
||||
setProjectId(id); setError("");
|
||||
try {
|
||||
setProject(await request(`/projects/${encodeURIComponent(id)}`));
|
||||
setProject(await send("GET", `/projects/${encodeURIComponent(id)}`));
|
||||
history.replaceState({}, "", `?project=${encodeURIComponent(id)}`);
|
||||
} catch (e: any) {setError(e.message);}
|
||||
}
|
||||
@@ -86,7 +92,9 @@ export default function TrajectoryCenterPage() {
|
||||
{projects.map((item) => <option key={item.id} value={item.id}>{item.id} · {item.customer || ""}</option>)}
|
||||
</select>
|
||||
</label>
|
||||
{project && <TrajectoryPanel key={project.id} project={project} request={request}/>}
|
||||
{project && <TrajectoryPanel key={project.id} project={project} send={send}/>}
|
||||
{project && <ImprovementPanel key={`imp-${project.id}`} project={project}
|
||||
identity={identity} send={send}/>}
|
||||
</>}
|
||||
</main>
|
||||
</div>;
|
||||
|
||||
Reference in New Issue
Block a user