Adds server-enforced convoy transitions with gate actor guards, convoy_events logging, and six MCP tools (create, brief, transition, log, query, reconcile). Co-authored-by: Cursor <cursoragent@cursor.com>
78 lines
1.9 KiB
TypeScript
78 lines
1.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { ConvoyTransitionError, validateConvoyTransition } from "./state-machine";
|
|
|
|
describe("validateConvoyTransition", () => {
|
|
it("rejects draft → released", () => {
|
|
expect(() =>
|
|
validateConvoyTransition({
|
|
from: "draft",
|
|
to: "released",
|
|
actor: "human-gate-1",
|
|
}),
|
|
).toThrow(ConvoyTransitionError);
|
|
|
|
try {
|
|
validateConvoyTransition({
|
|
from: "draft",
|
|
to: "released",
|
|
actor: "human-gate-1",
|
|
});
|
|
} catch (error) {
|
|
expect(error).toBeInstanceOf(ConvoyTransitionError);
|
|
expect((error as ConvoyTransitionError).code).toBe("INVALID_TRANSITION");
|
|
}
|
|
});
|
|
|
|
it("rejects role-architect on human-gate-1 transition", () => {
|
|
expect(() =>
|
|
validateConvoyTransition({
|
|
from: "convoy_review",
|
|
to: "ready",
|
|
actor: "role-architect",
|
|
}),
|
|
).toThrow(ConvoyTransitionError);
|
|
|
|
try {
|
|
validateConvoyTransition({
|
|
from: "convoy_review",
|
|
to: "ready",
|
|
actor: "role-architect",
|
|
});
|
|
} catch (error) {
|
|
expect((error as ConvoyTransitionError).code).toBe("FORBIDDEN_ACTOR");
|
|
}
|
|
});
|
|
|
|
it("allows human-gate-1 to approve plan", () => {
|
|
expect(() =>
|
|
validateConvoyTransition({
|
|
from: "convoy_review",
|
|
to: "ready",
|
|
actor: "human-gate-1",
|
|
}),
|
|
).not.toThrow();
|
|
});
|
|
|
|
it("allows role-architect to submit plan for review", () => {
|
|
expect(() =>
|
|
validateConvoyTransition({
|
|
from: "draft",
|
|
to: "convoy_review",
|
|
actor: "role-architect",
|
|
}),
|
|
).not.toThrow();
|
|
});
|
|
|
|
it("requires reason for blocked transitions", () => {
|
|
try {
|
|
validateConvoyTransition({
|
|
from: "ready",
|
|
to: "blocked",
|
|
actor: "role-implementer",
|
|
});
|
|
} catch (error) {
|
|
expect((error as ConvoyTransitionError).code).toBe("REASON_REQUIRED");
|
|
}
|
|
});
|
|
});
|