feat(investigation): publish incremental reference graphs
Publish graph deltas using the Server-owned cumulative graph contract. Validate historical comparison chart publications and consolidate Agent documentation.
This commit is contained in:
@@ -2,20 +2,11 @@ import { describe, expect, it } from "bun:test";
|
||||
|
||||
import publishPresentation from "../../.opencode/tools/publish_task_presentation.js";
|
||||
import publishProgress from "../../.opencode/tools/publish_task_progress.js";
|
||||
import { taskPublicationCommandSchema, taskPublicationProgressSchema } from "../../.opencode/tools/task-publication-schema.js";
|
||||
import { taskPublicationCommandSchema, taskPublicationGraphProgressSchema } from "../../.opencode/tools/task-publication-schema.js";
|
||||
import { taskProgressSchema } from "../../src/product/investigation-report.js";
|
||||
import { taskPresentationCommandSchema } from "../../src/product/task-presentation.js";
|
||||
|
||||
describe("task presentation input boundaries", () => {
|
||||
it("aligns native and product progress schemas for legacy and current diagrams", () => {
|
||||
const progress = { summary: "实际读取监测资料", diagram: { title: "当前路线", mermaid: "flowchart TD\nQUERY --> REVIEW", node_refs: [
|
||||
{ node_id: "QUERY", label: "查询", status: "running", map_targets: [{ source_id: "pipes", feature_id: "P-1" }] },
|
||||
{ node_id: "REVIEW", label: "复核", status: null },
|
||||
] } };
|
||||
expect(taskProgressSchema.parse(taskPublicationProgressSchema.parse(progress))).toEqual(taskProgressSchema.parse(progress));
|
||||
expect(taskProgressSchema.parse(taskPublicationProgressSchema.parse({ summary: "当前无图" }))).toEqual({ summary: "当前无图", diagram: null });
|
||||
});
|
||||
|
||||
it("accepts finite display commands and rejects unsupported data, layers and chart evidence", () => {
|
||||
const chart = { title: "压力", chart_type: "line", x_data: ["10:00"], series: [{ name: "MP01", data: [0.4] }] };
|
||||
const commands = [
|
||||
@@ -38,6 +29,41 @@ describe("task presentation input boundaries", () => {
|
||||
]) expect(() => taskPresentationCommandSchema.parse(command)).toThrow();
|
||||
});
|
||||
|
||||
it("exposes incremental graph updates instead of generated Mermaid to the Agent", () => {
|
||||
const update = { summary: "首次定位超时,调整窗口重试", graph_update: {
|
||||
nodes: [{ node_id: "locate_1", label: "首次定位", status: "failed", detail: "计算超时" },
|
||||
{ node_id: "locate_2", label: "调整窗口重试", status: "reference" }],
|
||||
edges: [{ source: "locate_1", target: "locate_2", label: "重试" }],
|
||||
} };
|
||||
expect(taskProgressSchema.parse(taskPublicationGraphProgressSchema.parse(update)))
|
||||
.toEqual(taskProgressSchema.parse(update));
|
||||
expect(taskPublicationGraphProgressSchema.safeParse({ summary: "重新生成", diagram: {
|
||||
title: "替换图", mermaid: "flowchart TD\nA[新图]",
|
||||
} }).success).toBe(false);
|
||||
expect(taskProgressSchema.safeParse({ ...update, diagram: {
|
||||
title: "替换图", mermaid: "flowchart TD\nA[新图]",
|
||||
} }).success).toBe(false);
|
||||
expect(taskProgressSchema.parse(taskPublicationGraphProgressSchema.parse({ summary: "继续等待" })))
|
||||
.toEqual({ summary: "继续等待", diagram: null });
|
||||
});
|
||||
|
||||
it("accepts optional reference annotations and rejects live execution states in new graph updates", () => {
|
||||
for (const status of [undefined, "reference", "recorded", "needs_input", "failed", "not_taken"]) {
|
||||
const progress = { summary: "整理流程参考", graph_update: {
|
||||
nodes: [{ node_id: "check", label: "核查路线", ...(status ? { status } : {}) }], edges: [],
|
||||
} };
|
||||
expect(taskPublicationGraphProgressSchema.safeParse(progress).success).toBe(true);
|
||||
expect(taskProgressSchema.safeParse(progress).success).toBe(true);
|
||||
}
|
||||
for (const status of ["running", "completed", "pending", "waiting", "waiting_human", "skipped", "cancelled"]) {
|
||||
const progress = { summary: "整理流程参考", graph_update: {
|
||||
nodes: [{ node_id: "check", label: "核查路线", status }], edges: [],
|
||||
} };
|
||||
expect(taskPublicationGraphProgressSchema.safeParse(progress).success).toBe(false);
|
||||
expect(taskProgressSchema.safeParse(progress).success).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it("revalidates semantic chart and Mermaid inputs before sending a tool request", async () => {
|
||||
const request_id = "60000000-0000-4000-8000-000000000001";
|
||||
await expect(publishPresentation.execute({ request_id, command: {
|
||||
@@ -46,11 +72,11 @@ describe("task presentation input boundaries", () => {
|
||||
} }, { sessionID: "test" })).rejects.toThrow("series must align");
|
||||
await expect(publishProgress.execute({ request_id, progress: {
|
||||
summary: "查询", diagram: { title: "路线", mermaid: "flowchart TD; A-->B; click A callback", node_refs: [] },
|
||||
} }, { sessionID: "test" })).rejects.toThrow("Mermaid");
|
||||
} } as never, { sessionID: "test" })).rejects.toThrow();
|
||||
await expect(publishProgress.execute({ request_id, progress: {
|
||||
summary: "查询", diagram: { title: "路线", mermaid: "flowchart TD\nA-->B",
|
||||
node_refs: [{ node_id: "query", label: "查询", status: "running",
|
||||
evidence_refs: [], action_refs: [], map_targets: [] }] },
|
||||
} }, { sessionID: "test" })).rejects.toThrow("未在 Mermaid 源码中声明");
|
||||
} } as never, { sessionID: "test" })).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user