From e2cd1d1ae50adad2d9c4e2a065d3edcc5134faf5 Mon Sep 17 00:00:00 2001 From: jiayiyan <43424880@qq.com> Date: Mon, 18 May 2026 15:48:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20add=20template=20moderator=20table=20?= =?UTF-8?q?=E2=80=94=20office=20=E2=86=92=20differ=20(edit)=20/=20END=20(g?= =?UTF-8?q?enerate)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/moderator.test.ts | 57 +++++++++++++++++++ templates/document-editor/src/moderator.ts | 21 +++++++ 2 files changed, 78 insertions(+) create mode 100644 templates/document-editor/__tests__/moderator.test.ts create mode 100644 templates/document-editor/src/moderator.ts diff --git a/templates/document-editor/__tests__/moderator.test.ts b/templates/document-editor/__tests__/moderator.test.ts new file mode 100644 index 0000000..a122197 --- /dev/null +++ b/templates/document-editor/__tests__/moderator.test.ts @@ -0,0 +1,57 @@ +import { START } from "@uncaged/workflow-runtime"; +import { describe, expect, test } from "bun:test"; +import { documentEditorTable } from "../src/moderator.js"; +import type { DocumentEditorMeta } from "../src/roles.js"; +import type { ThreadContext } from "@uncaged/workflow-runtime"; + +function makeCtx(steps: Array<{ role: string; meta: Record }>): ThreadContext { + return { + threadId: "test-thread", + depth: 0, + bundleHash: "", + start: { role: START, content: "", meta: {}, timestamp: 0, parentState: null }, + steps: steps.map((s) => ({ ...s, contentHash: "", refs: [], timestamp: 0 })), + } as unknown as ThreadContext; +} + +const officeIsEditCondition = documentEditorTable.office?.find( + (t) => t.condition !== "FALLBACK" && (t.condition as { name: string }).name === "officeIsEdit", +)?.condition as { check: (ctx: ThreadContext) => boolean } | undefined; + +describe("officeIsEdit condition", () => { + test("returns false when no office step in context", () => { + const ctx = makeCtx([]); + expect(officeIsEditCondition?.check(ctx)).toBe(false); + }); + + test("returns false when office step is generate mode", () => { + const ctx = makeCtx([ + { role: "office", meta: { mode: "generate", outputDocx: "/out.docx", sourceDocx: null } }, + ]); + expect(officeIsEditCondition?.check(ctx)).toBe(false); + }); + + test("returns true when office step is edit mode", () => { + const ctx = makeCtx([ + { role: "office", meta: { mode: "edit", outputDocx: "/out.docx", sourceDocx: "/src.docx" } }, + ]); + expect(officeIsEditCondition?.check(ctx)).toBe(true); + }); +}); + +describe("documentEditorTable structure", () => { + test("START routes to office via FALLBACK", () => { + const startTransitions = documentEditorTable[START]; + expect(startTransitions).toHaveLength(1); + expect(startTransitions[0].condition).toBe("FALLBACK"); + expect(startTransitions[0].role).toBe("office"); + }); + + test("differ routes to END via FALLBACK", () => { + const { END } = require("@uncaged/workflow-runtime"); + const differTransitions = documentEditorTable.differ; + expect(differTransitions).toHaveLength(1); + expect(differTransitions[0].condition).toBe("FALLBACK"); + expect(differTransitions[0].role).toBe(END); + }); +}); diff --git a/templates/document-editor/src/moderator.ts b/templates/document-editor/src/moderator.ts new file mode 100644 index 0000000..9544aa2 --- /dev/null +++ b/templates/document-editor/src/moderator.ts @@ -0,0 +1,21 @@ +import { END, type ModeratorCondition, type ModeratorTable, START } from "@uncaged/workflow-runtime"; +import type { DocumentEditorMeta } from "./roles.js"; + +const officeIsEdit: ModeratorCondition = { + name: "officeIsEdit", + description: "The office step produced an edited document (not a generated one)", + check: (ctx) => { + const officeStep = ctx.steps.find((s) => s.role === "office"); + if (officeStep === undefined) return false; + return (officeStep.meta as { mode: string }).mode === "edit"; + }, +}; + +export const documentEditorTable: ModeratorTable = { + [START]: [{ condition: "FALLBACK", role: "office" }], + office: [ + { condition: officeIsEdit, role: "differ" }, + { condition: "FALLBACK", role: END }, + ], + differ: [{ condition: "FALLBACK", role: END }], +};