feat: upgrade investigation and presentation runtime
Generic Container CI/CD / test-build-publish (push) Successful in 1m6s
Agent CI/CD / build-test-publish-and-deploy (push) Successful in 1m7s

This commit is contained in:
2026-10-30 17:30:19 +08:00
parent 3b52a0ddab
commit 6336157ff4
104 changed files with 6541 additions and 961 deletions
+27 -1
View File
@@ -1,5 +1,6 @@
import { describe, expect, it } from "bun:test";
import { taskReportSchema, taskProgressSchema } from "../../src/product/investigation-report.js";
import { taskPublicationDiagramSchema, taskPublicationProgressSchema, taskReportUiBlocksSchema } from "../../.opencode/tools/task-publication-schema.js";
const report = { title: "调查报告", current_conclusion: "无法确定原因", conclusion_boundary: "仅有上游数据",
evidence_refs: ["query-1"], recommendations: [], pending_items: [], stage: "final", outcome: "insufficient_data",
@@ -26,6 +27,9 @@ describe("new investigation report writes", () => {
for (const mermaid of ['%%{init: {}}%%\nflowchart TD\nA-->B', 'flowchart TD\nclick A callback', 'flowchart TD; A-->B; click A callback', 'sequenceDiagram\nA->>B: 查询']) {
expect(() => taskReportSchema.parse({ ...report, diagram: { ...diagram, mermaid } })).toThrow();
}
const mismatched = { ...diagram, node_refs: [{ ...diagram.node_refs[0], node_id: "query" }] };
expect(() => taskReportSchema.parse({ ...report, diagram: mismatched })).toThrow("未在 Mermaid 源码中声明");
expect(taskPublicationDiagramSchema.safeParse(mismatched).success).toBe(false);
});
it("rejects downstream work or unsupported claims on insufficient-data exit", () => {
for (const changes of [{ recommendations: ["派采样工单"] }, { pending_items: ["等待人工"] },
@@ -43,13 +47,26 @@ describe("new investigation report writes", () => {
expect(progress.diagram?.node_refs[0]).toMatchObject({ status: "running", map_targets: [{ source_id: "scada", feature_id: "MP01" }] });
expect(taskReportSchema.parse({ ...report, diagram }).diagram).toEqual(progress.diagram);
expect(taskProgressSchema.parse({ summary: "调整调查方向", diagram: null }).diagram).toBeNull();
for (const change of [{ status: "waiting" }, { map_targets: [{ source_id: "drainage", feature_id: "X" }] },
for (const change of [{ status: "blocked" }, { map_targets: [{ source_id: "drainage", feature_id: "X" }] },
{ map_targets: [{ source_id: "scada", feature_id: "" }] }]) {
expect(() => taskProgressSchema.parse({ summary: "核对", diagram: {
...diagram, node_refs: [{ ...diagram.node_refs[0], ...change }],
} })).toThrow();
}
});
it("preserves waiting, unknown and evidence-confirmed states at every diagram boundary", () => {
const diagram = { title: "等待系统结果", mermaid: "flowchart LR\nDONE --> WAIT --> UNKNOWN --> UNSET", node_refs: [
{ node_id: "DONE", label: "查询完成", status: "completed", evidence_refs: ["query-1"] },
{ node_id: "WAIT", label: "等待系统结果", status: "waiting" },
{ node_id: "UNKNOWN", label: "未知", status: null },
{ node_id: "UNSET", label: "未确认" },
] };
const nodes = taskProgressSchema.parse({ summary: "等待系统结果", diagram }).diagram!.node_refs;
expect(nodes?.map((node) => node.status)).toEqual(["completed", "waiting", null, undefined]);
expect(taskReportSchema.parse({ ...report, diagram }).diagram?.node_refs).toEqual(nodes);
expect(taskPublicationDiagramSchema.parse(diagram).node_refs).toEqual(nodes);
expect(taskPublicationProgressSchema.parse({ summary: "等待系统结果", diagram }).diagram?.node_refs).toEqual(nodes);
});
it("preserves parallel branches with independent activity and a pending join", () => {
const mermaid = 'flowchart TD\nSTART[受理投诉] --> SPLIT{并行核查}\nSPLIT --> TICKETS[工单核查]\nSPLIT --> SCADA[监测核查]\nTICKETS --> JOIN[汇总证据]\nSCADA --> JOIN';
const diagram = { title: "并行调查", mermaid, node_refs: [
@@ -76,6 +93,15 @@ const uiBlock = {
};
describe("interactive report publication", () => {
it("accepts final and legacy blocks and rejects process or unknown phases at both publication boundaries", () => {
for (const phase of [undefined, "final", "process", "unknown"]) {
const block = phase === undefined ? uiBlock : { ...uiBlock, phase };
const blocks = [{ block, evidence_refs: ["query-1"] }];
const accepted = phase === undefined || phase === "final";
expect(taskReportSchema.safeParse({ ...report, ui_blocks: blocks }).success).toBe(accepted);
expect(taskReportUiBlocksSchema.safeParse(blocks).success).toBe(accepted);
}
});
it("preserves rich blocks and legacy reports without changing fixed facts", () => {
expect(taskReportSchema.parse(report).ui_blocks).toBeUndefined();
const parsed = taskReportSchema.parse({ ...report, ui_blocks: [{ block: uiBlock, evidence_refs: ["query-1"] }] });