fix: resume workflow on revision requests

This commit is contained in:
lda
2026-07-11 23:04:47 +07:00 Verified
parent 31fb170dba
commit d99447ebd7
30 changed files with 411 additions and 187 deletions
@@ -49,7 +49,7 @@ const demoController = (
play: vi.fn(),
next: vi.fn(async () => {}),
submitSelectedIssues: vi.fn(async () => {}),
cancelReview: vi.fn(async () => {}),
requestRevision: vi.fn(async () => {}),
restart: vi.fn(),
primeReplayToStage: vi.fn(),
...overrides,
@@ -128,17 +128,17 @@ describe("useTimelineAgent", () => {
expect(submitSelectedIssues).toHaveBeenCalledWith(["risk-1"], "Create the selected issue.");
});
it("cancels review through the timeline", async () => {
const cancelReview = vi.fn(async () => {});
it("requests revision through the timeline", async () => {
const requestRevision = vi.fn(async () => {});
const demo = demoController({
state: { ...initialDemoTimelineState, phase: "review" },
cancelReview,
requestRevision,
});
const { result } = renderHook(() => useTimelineAgent(demo, { mode: "live", status: readyStatus }));
await act(async () => result.current.cancelReview());
await act(async () => result.current.requestRevision());
expect(cancelReview).toHaveBeenCalledWith("Cancelled by operator.");
expect(requestRevision).toHaveBeenCalledWith("Request revisions before creating issues.");
});
it("disables run when the timeline cannot start", () => {
@@ -147,19 +147,19 @@ describe("useTimelineAgent", () => {
expect(result.current.canRun).toBe(false);
});
it("does not advance replay cancellation into the submitted recording branch", async () => {
const cancelReview = vi.fn(async () => {});
it("advances replay revision requests through the negative branch", async () => {
const requestRevision = vi.fn(async () => {});
const next = vi.fn(async () => {});
const demo = demoController({
state: { ...initialDemoTimelineState, mode: "replay", phase: "review" },
cancelReview,
requestRevision,
next,
});
const { result } = renderHook(() => useTimelineAgent(demo, { mode: "replay", status: replayStatus }));
await act(async () => result.current.cancelReview());
await act(async () => result.current.requestRevision());
expect(cancelReview).toHaveBeenCalledWith("Cancelled by operator.");
expect(requestRevision).toHaveBeenCalledWith("Request revisions before creating issues.");
expect(next).not.toHaveBeenCalled();
expect(result.current.messages.at(-1)?.parts).toEqual(
expect.arrayContaining([
@@ -168,20 +168,20 @@ describe("useTimelineAgent", () => {
);
});
it("live cancel does not call next", async () => {
const cancelReview = vi.fn(async () => {});
it("live revision request advances the timeline", async () => {
const requestRevision = vi.fn(async () => {});
const next = vi.fn(async () => {});
const demo = demoController({
state: { ...initialDemoTimelineState, mode: "live", phase: "review" },
cancelReview,
requestRevision,
next,
});
const { result } = renderHook(() => useTimelineAgent(demo, { mode: "live", status: readyStatus }));
await act(async () => result.current.cancelReview());
await act(async () => result.current.requestRevision());
expect(cancelReview).toHaveBeenCalledWith("Cancelled by operator.");
expect(next).not.toHaveBeenCalled();
expect(requestRevision).toHaveBeenCalledWith("Request revisions before creating issues.");
expect(next).toHaveBeenCalledOnce();
});
it("uses replay label when live target failed", () => {
@@ -22,7 +22,7 @@ export type TimelineAgentController = {
readonly runLabel: string;
readonly runPreparedWorkflow: () => Promise<void>;
readonly submitSelectedIssues: () => Promise<void>;
readonly cancelReview: () => Promise<void>;
readonly requestRevision: () => Promise<void>;
};
const DEFAULT_COMMENT = "Create the selected issue.";
@@ -120,16 +120,15 @@ export const useTimelineAgent = (
));
}, [demo, selectedIssueIds]);
const cancelReview = useCallback(async () => {
await demo.cancelReview("Cancelled by operator.");
// Cancellation is terminal in presentation mode. Do not call next() or the UI
// would falsely advance into submitted/resume evidence.
const requestRevision = useCallback(async () => {
await demo.requestRevision("Request revisions before creating issues.");
if (modeLabel === "live") await demo.next();
setMessages((current) => appendToolMessage(
current,
"timeline-agent-cancel",
"timeline-agent-revision",
"resumeIssueReview",
{},
{ outcome: "cancelled" },
{ approved: false, outcome: "cancelled" },
{ outcome: "cancelled", label: "revision requested" },
));
}, [demo]);
@@ -139,6 +138,6 @@ export const useTimelineAgent = (
runLabel,
runPreparedWorkflow,
submitSelectedIssues,
cancelReview,
requestRevision,
};
};