import { describe, expect, test } from "bun:test"; import localforage from "localforage"; import { createGenerationTaskSubscriptionService, type GenerationTask } from "../src/services/api/task-center"; import { createGenerationTaskMaterializer, createIdempotentMaterializeOutput, materializeEffectKey, type GenerationTaskEffectClaim, type GenerationTaskEffectStore } from "../src/services/generation-task-materializer"; import { createLocalDreaminaTaskEffectStore } from "../src/services/local-dreamina-generation"; import { applyCanvasGenerationTaskNodeEffect, persistCanvasAgentGenerationContinuationEffect, persistCanvasGenerationEffect } from "../src/services/canvas-generation-consumer"; import { canvasCinematicContinuationEntryAdapters } from "../src/components/canvas/canvas-assistant-panel"; import { applyGenerationConsumerEffect, generationEffectApplied } from "../src/services/generation-consumer-dedupe"; import { createProviderNeutralGenerationTaskEffectStore } from "../src/services/provider-neutral-generation-effects"; import { consumeGenerationTaskAgent, consumeGenerationTaskMessage, consumeGenerationTaskNode, materializeGenerationTaskAssets } from "../src/services/project-asset-sync"; import { flushCanvasStorePersistence, useCanvasStore, withCanvasStorePersistenceSuppressed, type CanvasProject } from "../src/stores/canvas/use-canvas-store"; import { flushAssetStorePersistence, useAssetStore, type NewAsset } from "../src/stores/use-asset-store"; import { CanvasNodeType, type CanvasAssistantSession, type CanvasNodeData } from "../src/types/canvas"; import { recoverCanvasGenerationTaskNode } from "../src/pages/canvas/use-canvas-generation"; import { getActiveUserScope, setActiveUserScope } from "../src/lib/user-scope"; import { readImageMeta } from "../src/lib/image-utils"; import { consumeCanvasAgentGenerationContinuation } from "../src/pages/canvas/use-canvas-agent-operations"; function createEffectStore(): GenerationTaskEffectStore { const completed = new Map(); const claimed = new Set(); return { async claim(effectKey): Promise { const result = completed.get(effectKey); if (result) return { status: "completed", result }; if (claimed.has(effectKey)) return { status: "busy" }; claimed.add(effectKey); return { status: "claimed", fence: 1 }; }, async renew() { return { fence: 1 }; }, async complete(effectKey, _taskId, result) { claimed.delete(effectKey); completed.set(effectKey, result); }, async release(effectKey) { claimed.delete(effectKey); }, }; } type MetaImageHarnessImage = { naturalWidth: number; naturalHeight: number; onload: (() => void) | null; onerror: (() => void) | null; }; function installImageMetaHarness(naturalWidth = 0, naturalHeight = 0) { const originalImage = Object.getOwnPropertyDescriptor(globalThis, "Image"); const originalSetTimeout = Object.getOwnPropertyDescriptor(globalThis, "setTimeout"); const originalClearTimeout = Object.getOwnPropertyDescriptor(globalThis, "clearTimeout"); const images: MetaImageHarnessImage[] = []; const srcAssignments: string[] = []; const timers = new Map void>(); let nextTimer = 0; Object.defineProperty(globalThis, "Image", { configurable: true, value: class FakeImage implements MetaImageHarnessImage { naturalWidth = naturalWidth; naturalHeight = naturalHeight; onload: (() => void) | null = null; onerror: (() => void) | null = null; constructor() { images.push(this); } set src(value: string) { srcAssignments.push(value); } }, }); Object.defineProperty(globalThis, "setTimeout", { configurable: true, value: (handler: () => void) => { nextTimer += 1; timers.set(nextTimer, handler); return nextTimer; }, }); Object.defineProperty(globalThis, "clearTimeout", { configurable: true, value: (timer: number) => { timers.delete(timer); }, }); return { images, srcAssignments, timers, fireNextTimer() { const handler = timers.values().next().value as (() => void) | undefined; handler?.(); }, restore() { if (originalImage) Object.defineProperty(globalThis, "Image", originalImage); else delete (globalThis as { Image?: unknown }).Image; if (originalSetTimeout) Object.defineProperty(globalThis, "setTimeout", originalSetTimeout); if (originalClearTimeout) Object.defineProperty(globalThis, "clearTimeout", originalClearTimeout); }, }; } function trackedAbortSignal() { let aborted = false; const listeners = new Set(); const signal = { get aborted() { return aborted; }, addEventListener(type: string, listener: EventListenerOrEventListenerObject) { if (type === "abort") listeners.add(listener); }, removeEventListener(type: string, listener: EventListenerOrEventListenerObject) { if (type === "abort") listeners.delete(listener); }, } as unknown as AbortSignal; return { signal, abort() { aborted = true; const event = new Event("abort"); for (const listener of [...listeners]) { if (typeof listener === "function") listener(event); else listener.handleEvent(event); } }, listenerCount: () => listeners.size, }; } describe("readImageMeta", () => { test("success returns intrinsic metadata and releases every callback", async () => { const harness = installImageMetaHarness(2560, 1440); const abort = trackedAbortSignal(); try { const result = readImageMeta("data:image/webp;base64,AA==", abort.signal); harness.images[0]?.onload?.(); expect(await result).toEqual({ width: 2560, height: 1440, mimeType: "image/webp" }); expect(harness.images[0]?.onload).toBeNull(); expect(harness.images[0]?.onerror).toBeNull(); expect(harness.timers.size).toBe(0); expect(abort.listenerCount()).toBe(0); expect(harness.srcAssignments).toEqual(["data:image/webp;base64,AA=="]); } finally { harness.restore(); } }); test("image error falls back once and releases every callback", async () => { const harness = installImageMetaHarness(); const abort = trackedAbortSignal(); try { const result = readImageMeta("data:image/png;base64,AA==", abort.signal); harness.images[0]?.onerror?.(); expect(await result).toEqual({ width: 1024, height: 1024, mimeType: "image/png" }); expect(harness.images[0]?.onload).toBeNull(); expect(harness.images[0]?.onerror).toBeNull(); expect(harness.timers.size).toBe(0); expect(abort.listenerCount()).toBe(0); expect(harness.srcAssignments).toEqual(["data:image/png;base64,AA=="]); } finally { harness.restore(); } }); test("timeout falls back once and releases every callback", async () => { const harness = installImageMetaHarness(); const abort = trackedAbortSignal(); try { const result = readImageMeta("opaque://image", abort.signal); harness.fireNextTimer(); expect(await result).toEqual({ width: 1024, height: 1024, mimeType: "image/png" }); expect(harness.images[0]?.onload).toBeNull(); expect(harness.images[0]?.onerror).toBeNull(); expect(harness.timers.size).toBe(0); expect(abort.listenerCount()).toBe(0); expect(harness.srcAssignments).toEqual(["opaque://image", ""]); } finally { harness.restore(); } }); test("abort rejects promptly and releases every callback", async () => { const harness = installImageMetaHarness(); const abort = trackedAbortSignal(); try { const result = readImageMeta("opaque://image", abort.signal); const outcome = result.then( () => "resolved", (error: DOMException) => error.name, ); abort.abort(); const promptOutcome = await Promise.race([outcome, new Promise((resolve) => queueMicrotask(() => resolve("pending")))]); if (promptOutcome === "pending") harness.fireNextTimer(); await outcome; expect(promptOutcome).toBe("AbortError"); expect(harness.images[0]?.onload).toBeNull(); expect(harness.images[0]?.onerror).toBeNull(); expect(harness.timers.size).toBe(0); expect(abort.listenerCount()).toBe(0); expect(harness.srcAssignments).toEqual(["opaque://image", ""]); } finally { harness.restore(); } }); }); describe("generation task materializer", () => { test("remote Backend Create uses the default production materializer without Dreamina authority", async () => { const previousAssets = useAssetStore.getState().assets; useAssetStore.getState().replaceAssets([ { id: "asset-remote-default-wiring", kind: "image", title: "remote generated image", coverUrl: "opaque://remote-generated-image", tags: ["generated"], metadata: {}, data: { dataUrl: "opaque://remote-generated-image", width: 1, height: 1, bytes: 1, mimeType: "image/png", }, createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }, ]); const task: GenerationTask = { id: "backend-create-remote-default-wiring", provider: "remote-image-provider", type: "image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "READY", outputs: [ { outputIndex: 0, mediaType: "image", materializedAssetId: "asset-remote-default-wiring", }, ], createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; let attachments = 0; try { const materialized = await consumeGenerationTaskMessage(task, "message-remote-default-wiring", async ({ resultUrls }) => { attachments += 1; expect(resultUrls).toEqual(["opaque://remote-generated-image"]); }); expect(materialized.id).toBe(task.id); expect(attachments).toBe(1); } finally { useAssetStore.getState().replaceAssets(previousAssets); } }); test("image materialization decodes intrinsic Blob dimensions when provider metadata omits width and height", async () => { const previousAssets = useAssetStore.getState().assets; const originalWindow = (globalThis as { window?: unknown }).window; const originalImage = (globalThis as { Image?: unknown }).Image; const originalLocks = Object.getOwnPropertyDescriptor(navigator, "locks"); const originalGetItem = localforage.getItem.bind(localforage); const originalSetItem = localforage.setItem.bind(localforage); const localStorageValues = new Map(); const durableValues = new Map(); const lockTails = new Map>(); const imageBlob = new Blob([''], { type: "image/svg+xml" }); const imageUrl = URL.createObjectURL(imageBlob); Object.defineProperty(globalThis, "window", { configurable: true, value: { localStorage: { getItem: (key: string) => localStorageValues.get(key) ?? null, setItem: (key: string, value: string) => localStorageValues.set(key, value), removeItem: (key: string) => localStorageValues.delete(key), }, }, }); setActiveUserScope("materializer-indexeddb"); Object.defineProperty(navigator, "locks", { configurable: true, value: { async request(name: string, callback: () => Promise) { const prior = lockTails.get(name) ?? Promise.resolve(); let release!: () => void; const tail = new Promise((resolve) => { release = resolve; }); const queued = prior.then(() => tail); lockTails.set(name, queued); await prior; try { return await callback(); } finally { release(); if (lockTails.get(name) === queued) lockTails.delete(name); } }, }, }); localforage.getItem = (async (key: string) => durableValues.get(key) ?? null) as typeof localforage.getItem; localforage.setItem = (async (key: string, value: unknown) => { durableValues.set(key, value); return value; }) as typeof localforage.setItem; Object.defineProperty(globalThis, "Image", { configurable: true, value: class FakeImage { naturalWidth = 2560; naturalHeight = 1440; onload: (() => void) | null = null; onerror: (() => void) | null = null; set src(_value: string) { queueMicrotask(() => this.onload?.()); } }, }); const task: GenerationTask = { id: "backend-image-intrinsic-dimensions", provider: "remote-image-provider", type: "image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "PENDING_MATERIALIZATION", resultJson: JSON.stringify({ images: [ { dataUrl: imageUrl, storageKey: "resource:intrinsic-dimensions-existing", width: 1280, bytes: imageBlob.size, mimeType: imageBlob.type, }, ], }), createdAt: "2026-08-14T00:00:00.000Z", updatedAt: "2026-08-14T00:00:00.000Z", }; useAssetStore.getState().replaceAssets([]); try { const materialized = await materializeGenerationTaskAssets(task); const assetId = materialized.outputs?.[0]?.materializedAssetId; const asset = useAssetStore.getState().assets.find((candidate) => candidate.id === assetId); expect(asset).toMatchObject({ kind: "image", data: { width: 1280, height: 1440, }, }); const assetStoreKey = "infinite-canvas:asset_store:user:materializer-indexeddb"; expect(durableValues.has(assetStoreKey)).toBe(true); expect(localStorageValues.has(assetStoreKey)).toBe(false); } finally { URL.revokeObjectURL(imageUrl); localforage.getItem = originalGetItem; localforage.setItem = originalSetItem; if (originalLocks) Object.defineProperty(navigator, "locks", originalLocks); else delete (navigator as { locks?: unknown }).locks; if (originalImage === undefined) delete (globalThis as { Image?: unknown }).Image; else Object.defineProperty(globalThis, "Image", { configurable: true, value: originalImage }); if (originalWindow === undefined) delete (globalThis as { window?: unknown }).window; else Object.defineProperty(globalThis, "window", { configurable: true, value: originalWindow }); useAssetStore.getState().replaceAssets(previousAssets); } }); test("image materialization keeps its effect retryable when the IndexedDB catalog write fails", async () => { const previousAssets = useAssetStore.getState().assets; const originalWindow = (globalThis as { window?: unknown }).window; const originalLocks = Object.getOwnPropertyDescriptor(navigator, "locks"); const originalGetItem = localforage.getItem.bind(localforage); const originalSetItem = localforage.setItem.bind(localforage); const durableValues = new Map(); const localStorageValues = new Map(); const localStorageCalls: string[] = []; let failWrites = true; Object.defineProperty(globalThis, "window", { configurable: true, value: { localStorage: { getItem: (key: string) => { if (key.includes("infinite-canvas:asset_store")) localStorageCalls.push(`get:${key}`); return localStorageValues.get(key) ?? null; }, setItem: (key: string, value: string) => { if (key.includes("infinite-canvas:asset_store")) localStorageCalls.push(`set:${key}`); localStorageValues.set(key, value); }, removeItem: (key: string) => { if (key.includes("infinite-canvas:asset_store")) localStorageCalls.push(`remove:${key}`); localStorageValues.delete(key); }, }, }, }); setActiveUserScope("materializer-catalog-failure"); Object.defineProperty(navigator, "locks", { configurable: true, value: { request(_name: string, callback: () => Promise) { return callback(); }, }, }); localforage.getItem = (async (key: string) => durableValues.get(key) ?? null) as typeof localforage.getItem; localforage.setItem = (async (key: string, value: string) => { if (failWrites && key.includes("infinite-canvas:asset_store")) throw new Error("indexeddb catalog write failed"); durableValues.set(key, value); return value; }) as typeof localforage.setItem; const task: GenerationTask = { id: "backend-image-catalog-failure", provider: "remote-image-provider", type: "image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "PENDING_MATERIALIZATION", resultJson: JSON.stringify({ images: [ { dataUrl: "opaque://catalog-failure", storageKey: "resource:catalog-failure-existing", width: 640, height: 360, bytes: 12, mimeType: "image/png", }, ], }), createdAt: "2026-08-14T00:00:00.000Z", updatedAt: "2026-08-14T00:00:00.000Z", }; useAssetStore.getState().replaceAssets([]); try { await expect(materializeGenerationTaskAssets(task)).rejects.toThrow("indexeddb catalog write failed"); const assetStoreKey = "infinite-canvas:asset_store:user:materializer-catalog-failure"; expect(durableValues.has(assetStoreKey)).toBe(false); expect(localStorageCalls).toEqual([]); const releasedEffect = [...durableValues.entries()].find(([key]) => key.includes("generation-effect") && key.includes(task.id)); expect(releasedEffect).toBeDefined(); expect(JSON.parse(releasedEffect![1])).toMatchObject({ state: "released" }); failWrites = false; const recovered = await materializeGenerationTaskAssets(task); const recoveredId = recovered.outputs?.[0]?.materializedAssetId; const persisted = JSON.parse(durableValues.get(assetStoreKey)!) as { state: { assets: Array<{ id: string }> } }; expect(recovered.resultState).toBe("READY"); expect(persisted.state.assets.filter((candidate) => candidate.id === recoveredId)).toHaveLength(1); expect(JSON.parse(durableValues.get(releasedEffect![0])!)).toMatchObject({ state: "completed", result: { materializedAssetId: recoveredId } }); } finally { failWrites = false; useAssetStore.getState().replaceAssets(previousAssets); localforage.getItem = originalGetItem; localforage.setItem = originalSetItem; if (originalLocks) Object.defineProperty(navigator, "locks", originalLocks); else delete (navigator as { locks?: unknown }).locks; if (originalWindow === undefined) delete (globalThis as { window?: unknown }).window; else Object.defineProperty(globalThis, "window", { configurable: true, value: originalWindow }); } }); test("two concurrent remote Web consumers use the default browser durable atomic authority", async () => { const originalWindow = (globalThis as { window?: unknown }).window; const originalLocks = Object.getOwnPropertyDescriptor(navigator, "locks"); const originalGetItem = localforage.getItem.bind(localforage); const originalSetItem = localforage.setItem.bind(localforage); const localStorageValues = new Map(); const durableValues = new Map(); const lockTails = new Map>(); Object.defineProperty(globalThis, "window", { configurable: true, value: { localStorage: { getItem: (key: string) => localStorageValues.get(key) ?? null, setItem: (key: string, value: string) => localStorageValues.set(key, value), removeItem: (key: string) => localStorageValues.delete(key), }, }, }); Object.defineProperty(navigator, "locks", { configurable: true, value: { async request(name: string, callback: () => Promise) { const prior = lockTails.get(name) ?? Promise.resolve(); let release!: () => void; const tail = new Promise((resolve) => { release = resolve; }); const queued = prior.then(() => tail); lockTails.set(name, queued); await prior; try { return await callback(); } finally { release(); if (lockTails.get(name) === queued) lockTails.delete(name); } }, }, }); localforage.getItem = (async (key: string) => durableValues.get(key) ?? null) as typeof localforage.getItem; localforage.setItem = (async (key: string, value: unknown) => { durableValues.set(key, value); return value; }) as typeof localforage.setItem; const task: GenerationTask = { id: "backend-cinematic-default-authority", provider: "remote-cinematic-provider", type: "agent_storyboard_rows", status: "succeeded", prompt: "redacted", attempts: 1, createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; let continuations = 0; try { await Promise.all([ consumeGenerationTaskAgent(task, "shared-continuation", async () => { continuations += 1; }), consumeGenerationTaskAgent(task, "shared-continuation", async () => { continuations += 1; }), ]); expect(continuations).toBe(1); expect([...durableValues.keys()].some((key) => key.includes("agent-resume:backend-cinematic-default-authority:shared-continuation"))).toBe(true); } finally { localforage.getItem = originalGetItem; localforage.setItem = originalSetItem; if (originalLocks) Object.defineProperty(navigator, "locks", originalLocks); else delete (navigator as { locks?: unknown }).locks; if (originalWindow === undefined) delete (globalThis as { window?: unknown }).window; else Object.defineProperty(globalThis, "window", { configurable: true, value: originalWindow }); } }); test("provider-neutral lease renew complete and release stay bound to the claim account", async () => { const originalWindow = (globalThis as { window?: unknown }).window; const originalLocks = Object.getOwnPropertyDescriptor(navigator, "locks"); const originalGetItem = localforage.getItem.bind(localforage); const originalSetItem = localforage.setItem.bind(localforage); const localStorageValues = new Map(); const durableValues = new Map(); const lockTails = new Map>(); Object.defineProperty(globalThis, "window", { configurable: true, value: { localStorage: { getItem: (key: string) => localStorageValues.get(key) ?? null, setItem: (key: string, value: string) => localStorageValues.set(key, value), removeItem: (key: string) => localStorageValues.delete(key), }, }, }); Object.defineProperty(navigator, "locks", { configurable: true, value: { async request(name: string, callback: () => Promise) { const prior = lockTails.get(name) ?? Promise.resolve(); let release!: () => void; const tail = new Promise((resolve) => { release = resolve; }); const queued = prior.then(() => tail); lockTails.set(name, queued); await prior; try { return await callback(); } finally { release(); if (lockTails.get(name) === queued) lockTails.delete(name); } }, }, }); localforage.getItem = (async (key: string) => durableValues.get(key) ?? null) as typeof localforage.getItem; localforage.setItem = (async (key: string, value: unknown) => { durableValues.set(key, value); return value; }) as typeof localforage.setItem; try { setActiveUserScope("account-A"); const store = createProviderNeutralGenerationTaskEffectStore({ leaseMs: 1_000 }); const completeKey = "attach-message:backend-scope-bound:message-safe-id:0"; const releaseKey = "attach-node:backend-scope-bound:node-safe-id:0"; expect(await store.claim(completeKey, "backend-scope-bound")).toMatchObject({ status: "claimed" }); setActiveUserScope("account-B"); expect(await store.renew(completeKey, "backend-scope-bound")).toEqual({ fence: 1 }); await store.complete(completeKey, "backend-scope-bound", {}); setActiveUserScope("account-A"); expect(await store.claim(releaseKey, "backend-scope-bound")).toMatchObject({ status: "claimed" }); setActiveUserScope("account-B"); expect(await store.renew(releaseKey, "backend-scope-bound")).toEqual({ fence: 1 }); await store.release(releaseKey, "backend-scope-bound"); const keys = [...durableValues.keys()]; expect(keys.length).toBeGreaterThanOrEqual(2); expect(keys.every((key) => key.includes(":user:account-A"))).toBe(true); expect(keys.some((key) => key.includes(":user:account-B"))).toBe(false); } finally { setActiveUserScope(null); localforage.getItem = originalGetItem; localforage.setItem = originalSetItem; if (originalLocks) Object.defineProperty(navigator, "locks", originalLocks); else delete (navigator as { locks?: unknown }).locks; if (originalWindow === undefined) delete (globalThis as { window?: unknown }).window; else Object.defineProperty(globalThis, "window", { configurable: true, value: originalWindow }); } }); test("aborting a busy effect waiter clears its timer and prevents later claim or sink", async () => { const originalSetTimeout = globalThis.setTimeout; const originalClearTimeout = globalThis.clearTimeout; let scheduledHandle: ReturnType | undefined; let clearedScheduledTimer = 0; let resolveScheduled!: () => void; const scheduled = new Promise((resolve) => { resolveScheduled = resolve; }); globalThis.setTimeout = ((handler: TimerHandler, timeout?: number, ...args: unknown[]) => { const handle = originalSetTimeout(handler, timeout, ...args); if (scheduledHandle === undefined && typeof timeout === "number" && timeout <= 250) { scheduledHandle = handle; resolveScheduled(); } return handle; }) as typeof setTimeout; globalThis.clearTimeout = ((handle?: ReturnType) => { if (scheduledHandle !== undefined && handle === scheduledHandle) clearedScheduledTimer += 1; return originalClearTimeout(handle); }) as typeof clearTimeout; let claims = 0; let sinks = 0; let completes = 0; const task: GenerationTask = { id: "backend-abort-busy-waiter", provider: "remote-image-provider", type: "image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "READY", outputs: [{ outputIndex: 0, mediaType: "image", materializedAssetId: "asset-abort-busy" }], createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; const effects: GenerationTaskEffectStore = { async claim() { claims += 1; return claims === 1 ? { status: "busy", retryAt: new Date(Date.now() + 25).toISOString() } : { status: "completed", result: {} }; }, async renew() { return { fence: 1 }; }, async complete() { completes += 1; }, async release() {}, }; const materializer = createGenerationTaskMaterializer({ effects, async materializeOutput() { throw new Error("already materialized"); }, }); const controller = new AbortController(); try { const run = materializer.attachNode( task, "node-abort-busy", 0, async () => { sinks += 1; }, controller.signal, ); await scheduled; controller.abort(); await expect(run).rejects.toMatchObject({ name: "AbortError" }); await new Promise((resolve) => originalSetTimeout(resolve, 50)); expect(clearedScheduledTimer).toBeGreaterThanOrEqual(1); expect(claims).toBe(1); expect(sinks).toBe(0); expect(completes).toBe(0); } finally { globalThis.setTimeout = originalSetTimeout; globalThis.clearTimeout = originalClearTimeout; } }); test("Create message automatically resumes a stale remote effect lease after restart", async () => { let nowMs = Date.parse("2026-08-13T00:00:00.000Z"); const now = () => new Date(nowMs); const task: GenerationTask = { id: "backend-create-stale-lease", provider: "remote-image-provider", type: "image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "READY", outputs: [{ outputIndex: 0, mediaType: "image", materializedAssetId: "asset-stale-create" }], createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; const effectKey = `attach-message:${task.id}:message-stale-create:0`; const crashedPage = createProviderNeutralGenerationTaskEffectStore({ now, leaseMs: 100 }); expect((await crashedPage.claim(effectKey, task.id)).status).toBe("claimed"); const restartedPage = createProviderNeutralGenerationTaskEffectStore({ now, leaseMs: 100 }); const dependencies = { effects: restartedPage, async materializeOutput() { throw new Error("already materialized"); }, async waitUntil(retryAt: string) { nowMs = Date.parse(retryAt); }, }; const materializer = createGenerationTaskMaterializer(dependencies); let messageEffects = 0; await consumeGenerationTaskMessage( task, "message-stale-create", async () => { messageEffects += 1; }, { materialize: async (input) => input, materializedUrls: () => ["opaque://materialized"], attachMessage: (input, messageId, outputIndex, consumer) => materializer.attachMessage(input, messageId, outputIndex, consumer), }, ); expect(messageEffects).toBe(1); }); test("Canvas node automatically leaves loading after a stale remote effect lease expires", async () => { let nowMs = Date.parse("2026-08-13T00:01:00.000Z"); const now = () => new Date(nowMs); const task: GenerationTask = { id: "backend-canvas-stale-lease", provider: "remote-image-provider", type: "canvas_image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "READY", outputs: [{ outputIndex: 0, mediaType: "image", materializedAssetId: "asset-stale-canvas" }], createdAt: "2026-08-13T00:01:00.000Z", updatedAt: "2026-08-13T00:01:00.000Z", }; const effectKey = `attach-node:${task.id}:node-stale-canvas:0`; const crashedPage = createProviderNeutralGenerationTaskEffectStore({ now, leaseMs: 100 }); expect((await crashedPage.claim(effectKey, task.id)).status).toBe("claimed"); const restartedPage = createProviderNeutralGenerationTaskEffectStore({ now, leaseMs: 100 }); const dependencies = { effects: restartedPage, async materializeOutput() { throw new Error("already materialized"); }, async waitUntil(retryAt: string) { nowMs = Date.parse(retryAt); }, }; const materializer = createGenerationTaskMaterializer(dependencies); let canvasStatus: "loading" | "success" = "loading"; await consumeGenerationTaskNode( task, "node-stale-canvas", 0, async () => { canvasStatus = "success"; }, { materialize: async (input) => input, attachNode: (input, nodeId, outputIndex, consumer) => materializer.attachNode(input, nodeId, outputIndex, consumer), }, ); expect(canvasStatus).toBe("success"); }); test("Create local and remote successes use the shared materializer and message consumer", async () => { const effects = createEffectStore(); const materializer = createGenerationTaskMaterializer({ effects, async materializeOutput() { throw new Error("outputs are already materialized"); }, }); const base = { type: "image", status: "succeeded" as const, prompt: "redacted", attempts: 1, resultState: "READY" as const, outputs: [ { outputIndex: 0, mediaType: "image" as const, materializedAssetId: "asset-stable-id", }, ], createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; const tasks: GenerationTask[] = [ { ...base, id: "dreamina:create-local-task" }, { ...base, id: "backend-create-remote-task" }, ]; let materializeCalls = 0; let messageEffects = 0; for (let replay = 0; replay < 3; replay += 1) { for (const task of tasks) { await consumeGenerationTaskMessage( task, "message-create-safe-id", async () => { messageEffects += 1; }, { async materialize(input) { materializeCalls += 1; return input; }, materializedUrls: () => ["asset-url-redacted"], attachMessage: (input, messageId, outputIndex, consumer) => materializer.attachMessage(input, messageId, outputIndex, consumer), }, ); } } expect(materializeCalls).toBe(6); expect(messageEffects).toBe(2); }); test("Canvas production node adapter replays three times with one attachment", async () => { const task: GenerationTask = { id: "dreamina:canvas-node-task", type: "canvas_image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "READY", outputs: [ { outputIndex: 0, mediaType: "image", materializedAssetId: "asset-node-id", }, ], createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; const materializer = createGenerationTaskMaterializer({ effects: createEffectStore(), async materializeOutput() { throw new Error("already materialized"); }, }); let attachments = 0; for (let replay = 0; replay < 3; replay += 1) { await consumeGenerationTaskNode( task, "node-safe-id", 0, async () => { attachments += 1; }, { materialize: async (input) => input, attachNode: (input, nodeId, outputIndex, consumer) => materializer.attachNode(input, nodeId, outputIndex, consumer), }, ); } expect(attachments).toBe(1); }); test("cinematic agent production adapter replays three times with one continuation", async () => { const task: GenerationTask = { id: "backend-cinematic-task", type: "agent_storyboard_rows", status: "succeeded", prompt: "redacted", attempts: 1, createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; const materializer = createGenerationTaskMaterializer({ effects: createEffectStore(), async materializeOutput() { throw new Error("no media outputs"); }, }); let continuations = 0; for (let replay = 0; replay < 3; replay += 1) { await consumeGenerationTaskAgent( task, "cinematic-continuation-id", async () => { continuations += 1; }, { resumeAgent: (input, continuationId, consumer) => materializer.resumeAgent(input, continuationId, consumer), }, ); } expect(continuations).toBe(1); }); test("post-write aborted cinematic commit completes the agent effect claim before replay", async () => { const originalWindow = (globalThis as { window?: unknown }).window; const originalGetItem = localforage.getItem.bind(localforage); const originalSetItem = localforage.setItem.bind(localforage); const previousScope = getActiveUserScope(); const previousProjects = useCanvasStore.getState().projects; const values = new Map(); const localStorageValues = new Map(); Object.defineProperty(globalThis, "window", { configurable: true, value: { localStorage: { getItem: (key: string) => localStorageValues.get(key) ?? null, setItem: (key: string, value: string) => localStorageValues.set(key, value), removeItem: (key: string) => localStorageValues.delete(key), }, }, }); let currentEffectKey = ""; let abortOnCommit = true; let parentController: AbortController | undefined; localforage.getItem = (async (key: string) => values.get(key) ?? null) as typeof localforage.getItem; localforage.setItem = (async (key: string, value: string) => { values.set(key, value); if (abortOnCommit && currentEffectKey && value.includes(currentEffectKey)) { abortOnCommit = false; parentController?.abort(); } return value; }) as typeof localforage.setItem; const scope = "cinematic-materializer-post-write-abort"; const projectId = "canvas-cinematic-materializer-post-write-abort"; const sessionId = "session-cinematic-materializer-post-write-abort"; const messageId = "message-cinematic-materializer-post-write-abort"; const continuationId = "cinematic-materializer-continuation"; const pendingSession: CanvasAssistantSession = { id: sessionId, title: "pending", pendingBackendSession: { id: "backend-cinematic-materializer", kind: "cinematic", messageId, status: "pending", startedAt: "2026-08-14T00:00:00.000Z" }, messages: [{ id: messageId, role: "assistant", text: "pending", detail: { kind: "cinematic", backendSessionId: "backend-cinematic-materializer", status: "pending" } }], createdAt: "2026-08-14T00:00:00.000Z", updatedAt: "2026-08-14T00:00:00.000Z", }; const project: CanvasProject = { id: projectId, title: "cinematic materializer", createdAt: "2026-08-14T00:00:00.000Z", updatedAt: "2026-08-14T00:00:00.000Z", nodes: [], connections: [], chatSessions: [pendingSession], activeChatId: sessionId, backgroundMode: "dots", showImageInfo: false, viewport: { x: 0, y: 0, k: 1 }, directorScenes: [], }; const task: GenerationTask = { id: "backend-cinematic-post-write-abort-task", type: "agent_storyboard_rows", status: "succeeded", prompt: "redacted", attempts: 1, createdAt: "2026-08-14T00:00:00.000Z", updatedAt: "2026-08-14T00:00:00.000Z", }; const materializer = createGenerationTaskMaterializer({ effects: createEffectStore(), async materializeOutput() { throw new Error("agent task has no media outputs"); }, }); let mountedSessions = [pendingSession]; let consumerCalls = 0; let executeCalls = 0; try { setActiveUserScope(scope); useCanvasStore.setState({ projects: [project] }); await flushCanvasStorePersistence(); for (let replay = 0; replay < 3; replay += 1) { const controller = new AbortController(); parentController = controller; await consumeGenerationTaskAgent( task, continuationId, async ({ effectKey, signal }) => { consumerCalls += 1; if (mountedSessions[0]?.generationEffectKeys?.includes(effectKey)) return; currentEffectKey = effectKey; await canvasCinematicContinuationEntryAdapters["online-tool"]({ projectId, effectKey, signal, readSnapshot: () => ({ projectId, title: "cinematic materializer", nodes: [], connections: [], selectedNodeIds: [], viewport: { x: 0, y: 0, k: 1 } }), executeOps: async () => { executeCalls += 1; }, completeSession: (key) => { mountedSessions = [ { ...pendingSession, pendingBackendSession: undefined, generationEffectKeys: key ? [key] : undefined, messages: [{ id: messageId, role: "assistant", text: "completed", detail: { kind: "cinematic", backendSessionId: "backend-cinematic-materializer", status: "completed" } }], updatedAt: "2026-08-14T00:01:00.000Z", }, ]; useCanvasStore.getState().updateProject(projectId, { chatSessions: mountedSessions }); return mountedSessions; }, readLiveSessionState: () => ({ sessions: mountedSessions, activeChatId: sessionId }), restoreLiveSessions: (sessions) => { mountedSessions = sessions; }, restoreLiveSnapshot: () => undefined, failProvider: () => { throw new Error("post-write abort must not become a provider failure"); }, }); }, { signal: controller.signal, resumeAgent: (input, id, consumer) => materializer.resumeAgent(input, id, consumer, controller.signal), }, ); } expect(consumerCalls).toBe(1); expect(executeCalls).toBe(1); expect(mountedSessions[0]?.generationEffectKeys?.length).toBe(1); expect(mountedSessions[0]?.pendingBackendSession).toBeUndefined(); } finally { setActiveUserScope(previousScope); withCanvasStorePersistenceSuppressed(() => useCanvasStore.setState({ projects: previousProjects })); localforage.getItem = originalGetItem; localforage.setItem = originalSetItem; if (originalWindow === undefined) delete (globalThis as { window?: unknown }).window; else Object.defineProperty(globalThis, "window", { configurable: true, value: originalWindow }); } }); test("online and local agents refresh-reconnect the original scoped task and resume once", async () => { for (const provider of ["backend-online", "dreamina-cli"] as const) { const effects = createEffectStore(); const materializer = createGenerationTaskMaterializer({ effects, async materializeOutput() { throw new Error("agent task has no media output"); }, }); let queryCalls = 0; let waitCalls = 0; let continuations = 0; let release!: () => void; const gate = new Promise((resolveGate) => { release = resolveGate; }); const running: GenerationTask = { id: provider === "dreamina-cli" ? "dreamina:agent-refresh-task-0001" : "backend-agent-refresh-task-0001", provider, projectId: "agent-project-0001", type: "agent_storyboard_rows", status: "running", prompt: "fixture", attempts: 1, clientContext: { conversationId: "conversation-agent-0001", messageId: "message-agent-0001", nodeId: "node-agent-0001", }, createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; const terminal = { ...running, status: "succeeded" as const, updatedAt: "2026-08-13T00:01:00.000Z" }; const service = createGenerationTaskSubscriptionService({ async queryTask() { queryCalls += 1; return running; }, async waitTask() { waitCalls += 1; await gate; return terminal; }, }); let continuationChain = Promise.resolve(); const observe = (task: GenerationTask) => { if (task.status !== "succeeded") return; continuationChain = continuationChain.then(() => consumeGenerationTaskAgent( task, `${task.clientContext?.conversationId}:${task.clientContext?.messageId}:${task.clientContext?.nodeId}`, async () => { continuations += 1; }, { resumeAgent: (input, continuationId, consumer) => materializer.resumeAgent(input, continuationId, consumer), }, ), ); }; const disconnect = service.subscribe([running.id], observe); await Promise.resolve(); disconnect(); const reconnect = service.subscribe([running.id], observe); release(); await new Promise((resolveTick) => setTimeout(resolveTick, 0)); await continuationChain; reconnect(); await consumeGenerationTaskAgent( terminal, "conversation-agent-0001:message-agent-0001:node-agent-0001", async () => { continuations += 1; }, { resumeAgent: (input, continuationId, consumer) => materializer.resumeAgent(input, continuationId, consumer), }, ); expect({ provider, queryCalls, waitCalls, continuations }).toEqual({ provider, queryCalls: 1, waitCalls: 1, continuations: 1, }); expect(terminal).toMatchObject({ projectId: "agent-project-0001", clientContext: { conversationId: "conversation-agent-0001", messageId: "message-agent-0001", nodeId: "node-agent-0001", }, }); } }); test("two Web clients use the Agent atomic effect authority", async () => { const leaseToken = "11111111-1111-4111-8111-111111111111"; let state: "available" | "claimed" | "completed" = "available"; const requests: Array<{ path: string; body: Record }> = []; const request = async (path: string, init?: RequestInit) => { const body = JSON.parse(String(init?.body || "{}")) as Record; requests.push({ path, body }); let result: Record; if (path.endsWith("/claim")) { result = state === "completed" ? { status: "completed", result: { materializedAssetId: "asset-agent-durable" } } : state === "claimed" ? { status: "busy", retryAt: "2026-08-13T00:00:30.000Z" } : ((state = "claimed"), { status: "claimed", leaseToken, leaseExpiresAt: "2026-08-13T00:00:30.000Z", fence: 1, }); } else if (path.endsWith("/renew")) { result = { leaseExpiresAt: "2026-08-13T00:00:40.000Z", fence: 1, }; } else if (path.endsWith("/complete")) { const completed = state === "claimed" && body.leaseToken === leaseToken; if (completed) state = "completed"; result = { completed }; } else { state = "available"; result = { released: true }; } return new Response(JSON.stringify({ ok: true, result }), { status: 200, headers: { "content-type": "application/json" }, }); }; const client = () => ({ connect: async () => ({ state: "connected" }) as never, request, }); const first = createLocalDreaminaTaskEffectStore({ client: client() }); const second = createLocalDreaminaTaskEffectStore({ client: client() }); const effectKey = "materialize:dreamina:task-cross-tab:0"; const taskId = "dreamina:task-cross-tab"; const claims = await Promise.all([first.claim(effectKey, taskId), second.claim(effectKey, taskId)]); expect(claims.map((claim) => claim.status).sort()).toEqual(["busy", "claimed"]); await first.complete(effectKey, taskId, { materializedAssetId: "asset-agent-durable" }); expect(requests.find((entry) => entry.path.endsWith("/complete"))?.body).toEqual({ consumerId: "web-generation-materializer", taskId, effectKey, leaseToken, fence: 1, result: { materializedAssetId: "asset-agent-durable" }, }); expect(await second.claim(effectKey, taskId)).toEqual({ status: "completed", result: { materializedAssetId: "asset-agent-durable" }, }); }); test("Web effect renewal carries the full task lease binding", async () => { const leaseToken = "22222222-2222-4222-8222-222222222222"; const taskId = "dreamina:web-renew-task"; const effectKey = "attach-message:dreamina:web-renew-task:message-safe-id:0"; const requests: Array<{ path: string; body: Record }> = []; const client = { connect: async () => ({ state: "connected" }) as never, request: async (path: string, init?: RequestInit) => { const body = JSON.parse(String(init?.body || "{}")) as Record; requests.push({ path, body }); const result = path.endsWith("/claim") ? { status: "claimed", leaseToken, leaseExpiresAt: "2026-08-13T00:00:30.000Z", fence: 7 } : { leaseExpiresAt: "2026-08-13T00:00:40.000Z", fence: 7 }; return new Response(JSON.stringify({ ok: true, result }), { status: 200, headers: { "content-type": "application/json" }, }); }, }; const store = createLocalDreaminaTaskEffectStore({ client }); expect(await store.claim(effectKey, taskId)).toEqual({ status: "claimed", fence: 7 }); expect(await store.renew(effectKey, taskId)).toEqual({ fence: 7 }); expect(requests.at(-1)).toEqual({ path: "/dreamina/generate/effects/renew", body: { consumerId: "web-generation-materializer", taskId, effectKey, leaseToken, fence: 7, }, }); }); test("a rejected release remains an explicit ownership error and keeps the local lease", async () => { const leaseToken = "33333333-3333-4333-8333-333333333333"; const taskId = "dreamina:web-release-task"; const effectKey = "agent-resume:dreamina:web-release-task:continuation-safe-id"; let releaseCalls = 0; const releaseBodies: Record[] = []; const client = { connect: async () => ({ state: "connected" }) as never, request: async (path: string, init?: RequestInit) => { if (path.endsWith("/release")) { releaseBodies.push(JSON.parse(String(init?.body || "{}")) as Record); } const result = path.endsWith("/claim") ? { status: "claimed", leaseToken, leaseExpiresAt: "2026-08-13T00:00:30.000Z", fence: 3 } : ((releaseCalls += 1), { released: false }); return new Response(JSON.stringify({ ok: true, result }), { status: 200, headers: { "content-type": "application/json" }, }); }, }; const store = createLocalDreaminaTaskEffectStore({ client }); await store.claim(effectKey, taskId); await expect(store.release(effectKey, taskId)).rejects.toMatchObject({ code: "local_runtime_effect_lease_lost" }); await expect(store.release(effectKey, taskId)).rejects.toMatchObject({ code: "local_runtime_effect_lease_lost" }); expect(releaseCalls).toBe(2); expect(releaseBodies).toEqual( [0, 1].map(() => ({ consumerId: "web-generation-materializer", taskId, effectKey, leaseToken, fence: 3, })), ); }); test("a slow consumer renews its lease across the original TTL and blocks a second instance", async () => { const task: GenerationTask = { id: "dreamina:slow-effect-task", type: "image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "READY", outputs: [{ outputIndex: 0, mediaType: "image", materializedAssetId: "asset-slow-effect" }], createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; let now = 0; let fence = 0; let active: { token: number; fence: number; expiresAt: number } | undefined; let completed = false; let resolveRenewed!: () => void; const renewed = new Promise((resolve) => { resolveRenewed = resolve; }); let resolveStarted!: () => void; const started = new Promise((resolve) => { resolveStarted = resolve; }); let resolveFinish!: () => void; const finish = new Promise((resolve) => { resolveFinish = resolve; }); const effectClient = (): GenerationTaskEffectStore => { let owned: { token: number; fence: number } | undefined; return { async claim() { if (completed) return { status: "completed", result: {} }; if (active && active.expiresAt > now) return { status: "busy", retryAt: new Date(active.expiresAt).toISOString() }; active = { token: (active?.token ?? 0) + 1, fence: ++fence, expiresAt: now + 100 }; owned = { token: active.token, fence: active.fence }; return { status: "claimed", fence: active.fence }; }, async renew() { if (!owned || !active || active.token !== owned.token || active.fence !== owned.fence || active.expiresAt <= now) { throw new Error("lease lost"); } active.expiresAt = now + 100; resolveRenewed(); return { fence: active.fence }; }, async complete() { if (!owned || !active || active.token !== owned.token || active.fence !== owned.fence || active.expiresAt <= now) { throw new Error("stale complete"); } completed = true; active = undefined; }, async release() { if (owned && active?.token === owned.token && active.fence === owned.fence) active = undefined; }, } as GenerationTaskEffectStore; }; const first = createGenerationTaskMaterializer({ effects: effectClient(), leaseHeartbeatMs: 1, async materializeOutput() { throw new Error("already materialized"); }, }); let firstRun!: ReturnType; const second = createGenerationTaskMaterializer({ effects: effectClient(), leaseHeartbeatMs: 1, async waitUntil() { await firstRun; }, async materializeOutput() { throw new Error("already materialized"); }, }); const applied = new Set(); let sideEffects = 0; const sink = async ({ effectKey }: { effectKey: string }) => { if (!applied.has(effectKey)) { applied.add(effectKey); sideEffects += 1; } resolveStarted(); await finish; }; firstRun = first.attachNode(task, "node-slow-effect", 0, sink); await started; now = 80; await Promise.race([renewed, new Promise((_, reject) => setTimeout(() => reject(new Error("lease was not renewed")), 100))]); now = 120; const secondRun = second.attachNode(task, "node-slow-effect", 0, sink); resolveFinish(); expect(await Promise.all([firstRun, secondRun])).toEqual(["applied", "completed"]); expect(sideEffects).toBe(1); }); test("lease renewal failure aborts an in-flight consumer before its later durable write and never completes the claim", async () => { const task: GenerationTask = { id: "task-renew-fencing", type: "image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "READY", outputs: [{ outputIndex: 0, mediaType: "image", materializedAssetId: "asset-renew-fencing" }], createdAt: "2026-08-14T00:00:00.000Z", updatedAt: "2026-08-14T00:00:00.000Z", }; let renewCalls = 0; let completeCalls = 0; let releaseCalls = 0; let durableWrites = 0; const parentController = new AbortController(); let consumerSignal: AbortSignal | undefined; let resolveRenewAttempted!: () => void; const renewAttempted = new Promise((resolve) => { resolveRenewAttempted = resolve; }); let releaseDurableWrite!: () => void; const durableWriteAllowed = new Promise((resolve) => { releaseDurableWrite = resolve; }); const effects: GenerationTaskEffectStore = { async claim() { return { status: "claimed", fence: 1 }; }, async renew() { renewCalls += 1; resolveRenewAttempted(); throw new Error("lease lost during consumer"); }, async complete() { completeCalls += 1; }, async release() { releaseCalls += 1; }, }; const materializer = createGenerationTaskMaterializer({ effects, leaseHeartbeatMs: 1, async materializeOutput() { throw new Error("already materialized"); }, }); const run = materializer.attachNode( task, "node-renew-fencing", 0, async ({ signal }) => { consumerSignal = signal; await durableWriteAllowed; if (!signal?.aborted) durableWrites += 1; }, parentController.signal, ); await renewAttempted; await new Promise((resolve) => { if (consumerSignal?.aborted) resolve(); else consumerSignal?.addEventListener("abort", () => resolve(), { once: true }); }); releaseDurableWrite(); await expect(run).rejects.toThrow("lease lost during consumer"); expect(renewCalls).toBeGreaterThan(0); expect(consumerSignal).toBeDefined(); expect(consumerSignal?.aborted).toBe(true); expect(parentController.signal.aborted).toBe(false); expect(durableWrites).toBe(0); expect(completeCalls).toBe(0); expect(releaseCalls).toBe(1); }); test("replaying one materialize effect three times inserts the asset once", async () => { const task: GenerationTask = { id: "task-safe-id", type: "image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "PENDING_MATERIALIZATION", outputs: [ { outputIndex: 0, mediaType: "image", providerArtifactRef: "provider-artifact-opaque", }, ], createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; const effects = createEffectStore(); let inserts = 0; const materializer = createGenerationTaskMaterializer({ effects, async materializeOutput({ effectKey, output }) { inserts += 1; expect(effectKey).toBe(materializeEffectKey(task.id, output.outputIndex)); return { materializedAssetId: "asset-stable-id" }; }, }); const results = []; for (let replay = 0; replay < 3; replay += 1) { results.push(await materializer.materialize(task)); } expect(inserts).toBe(1); expect(results.every((result) => result.outputs[0]?.materializedAssetId === "asset-stable-id")).toBe(true); expect(results.at(-1)?.resultState).toBe("READY"); }); test("a download crash keeps provider success orthogonal and remains retryable", async () => { const task: GenerationTask = { id: "task-download-crash", type: "image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "PENDING_MATERIALIZATION", outputs: [{ outputIndex: 0, mediaType: "image", providerArtifactRef: "provider-artifact-opaque" }], createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; const effects = createEffectStore(); let attempts = 0; const materializer = createGenerationTaskMaterializer({ effects, async materializeOutput() { attempts += 1; if (attempts === 1) throw new Error("download interrupted"); return { materializedAssetId: "asset-after-retry" }; }, }); await expect(materializer.materialize(task)).rejects.toThrow("download interrupted"); expect(task.status).toBe("succeeded"); expect(task.resultState).toBe("PENDING_MATERIALIZATION"); const recovered = await materializer.materialize(task); expect(attempts).toBe(2); expect(recovered.status).toBe("succeeded"); expect(recovered.resultState).toBe("READY"); }); test("two materializer instances atomically insert or return one asset for the same effect key", async () => { const task: GenerationTask = { id: "dreamina:concurrent-asset-task", type: "image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "PENDING_MATERIALIZATION", outputs: [{ outputIndex: 0, mediaType: "image", providerArtifactRef: "provider-artifact-opaque" }], createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; const previousAssets = useAssetStore.getState().assets; useAssetStore.getState().replaceAssets([]); const asset: NewAsset = { kind: "image", title: "redacted generated image", coverUrl: "opaque://materialized", tags: ["generated"], metadata: {}, data: { dataUrl: "opaque://materialized", width: 1, height: 1, bytes: 1, mimeType: "image/png", }, }; try { const materializeOutput = createIdempotentMaterializeOutput({ insertOrReturnAsset: ({ effectKey }) => useAssetStore.getState().addGenerationAsset(effectKey, asset), }); const first = createGenerationTaskMaterializer({ effects: createEffectStore(), materializeOutput }); const second = createGenerationTaskMaterializer({ effects: createEffectStore(), materializeOutput }); const results = await Promise.all([first.materialize(task), second.materialize(task)]); const effectKey = materializeEffectKey(task.id, 0); const stored = useAssetStore.getState().assets.filter((candidate) => candidate.metadata?.generationEffectKey === effectKey); const assetIds = results.map((result) => result.outputs?.[0]?.materializedAssetId); expect(stored).toHaveLength(1); expect(assetIds[0]).toMatch(/^generation_[0-9a-f]{64}$/); expect(assetIds[1]).toBe(assetIds[0]); expect(stored[0]?.id).toBe(assetIds[0]); } finally { useAssetStore.getState().replaceAssets(previousAssets); } }); test("a crash after asset insert reuses the asset before acknowledging the effect", async () => { const task: GenerationTask = { id: "task-insert-crash", type: "image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "PENDING_MATERIALIZATION", outputs: [{ outputIndex: 0, mediaType: "image", providerArtifactRef: "provider-artifact-opaque" }], createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; const completed = new Map(); const claimed = new Set(); let firstAck = true; const effects: GenerationTaskEffectStore = { async claim(effectKey) { const result = completed.get(effectKey); if (result) return { status: "completed", result }; if (claimed.has(effectKey)) return { status: "busy" }; claimed.add(effectKey); return { status: "claimed", fence: 1 }; }, async renew() { return { fence: 1 }; }, async complete(effectKey, _taskId, result) { if (firstAck) { firstAck = false; throw new Error("crash before effect ack"); } claimed.delete(effectKey); completed.set(effectKey, result); }, async release(effectKey) { claimed.delete(effectKey); }, }; const assets = new Map(); let inserts = 0; const materializer = createGenerationTaskMaterializer({ effects, materializeOutput: createIdempotentMaterializeOutput({ async insertOrReturnAsset({ effectKey }) { const existing = assets.get(effectKey); if (existing) return existing; inserts += 1; const assetId = "asset-inserted-once"; assets.set(effectKey, assetId); return assetId; }, }), }); await expect(materializer.materialize(task)).rejects.toThrow("crash before effect ack"); const recovered = await materializer.materialize(task); const replayed = await materializer.materialize(task); expect(inserts).toBe(1); expect(recovered.outputs[0]?.materializedAssetId).toBe("asset-inserted-once"); expect(replayed.outputs[0]?.materializedAssetId).toBe("asset-inserted-once"); }); test("replaying node message and agent effects three times applies each once", async () => { const task: GenerationTask = { id: "task-consumers", type: "image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "READY", outputs: [ { outputIndex: 0, mediaType: "image", providerArtifactRef: "provider-artifact-opaque", materializedAssetId: "asset-stable-id", }, ], createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; const effects = createEffectStore(); const materializer = createGenerationTaskMaterializer({ effects, async materializeOutput() { throw new Error("unexpected materialization"); }, }); const calls = { node: 0, message: 0, agent: 0 }; for (let replay = 0; replay < 3; replay += 1) { await materializer.attachNode(task, "node-safe-id", 0, async () => { calls.node += 1; }); await materializer.attachMessage(task, "message-safe-id", 0, async () => { calls.message += 1; }); await materializer.resumeAgent(task, "continuation-safe-id", async () => { calls.agent += 1; }); } expect(calls).toEqual({ node: 1, message: 1, agent: 1 }); }); test("attachment failures before side effects stay retryable without duplicates", async () => { const task: GenerationTask = { id: "task-attachment-crash", type: "image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "READY", outputs: [{ outputIndex: 0, mediaType: "image", materializedAssetId: "asset-stable-id" }], createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; const materializer = createGenerationTaskMaterializer({ effects: createEffectStore(), async materializeOutput() { throw new Error("unexpected materialization"); }, }); let nodeAttempts = 0; let messageAttempts = 0; let nodeAttachments = 0; let messageAttachments = 0; const attachNode = async () => { nodeAttempts += 1; if (nodeAttempts === 1) throw new Error("crash before node attachment"); nodeAttachments += 1; }; const attachMessage = async () => { messageAttempts += 1; if (messageAttempts === 1) throw new Error("crash before message attachment"); messageAttachments += 1; }; await expect(materializer.attachNode(task, "node-safe-id", 0, attachNode)).rejects.toThrow("crash before node attachment"); await expect(materializer.attachMessage(task, "message-safe-id", 0, attachMessage)).rejects.toThrow("crash before message attachment"); for (let replay = 0; replay < 3; replay += 1) { await materializer.attachNode(task, "node-safe-id", 0, attachNode); await materializer.attachMessage(task, "message-safe-id", 0, attachMessage); } expect(nodeAttachments).toBe(1); expect(messageAttachments).toBe(1); }); test("agent continuation ack crash replays without resuming twice", async () => { const task: GenerationTask = { id: "task-agent-ack-crash", type: "image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "READY", outputs: [{ outputIndex: 0, mediaType: "image", materializedAssetId: "asset-stable-id" }], createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; const completed = new Map(); const claimed = new Set(); let failFirstAgentAck = true; const effects: GenerationTaskEffectStore = { async claim(effectKey) { const result = completed.get(effectKey); if (result) return { status: "completed", result }; if (claimed.has(effectKey)) return { status: "busy" }; claimed.add(effectKey); return { status: "claimed", fence: 1 }; }, async renew() { return { fence: 1 }; }, async complete(effectKey, _taskId, result) { if (effectKey.startsWith("agent-resume:") && failFirstAgentAck) { failFirstAgentAck = false; throw new Error("crash before continuation ack"); } claimed.delete(effectKey); completed.set(effectKey, result); }, async release(effectKey) { claimed.delete(effectKey); }, }; const materializer = createGenerationTaskMaterializer({ effects, async materializeOutput() { throw new Error("unexpected materialization"); }, }); const resumed = new Set(); let resumes = 0; const resume = async ({ effectKey }: { effectKey: string }) => { if (resumed.has(effectKey)) return; resumes += 1; resumed.add(effectKey); }; await expect(materializer.resumeAgent(task, "continuation-safe-id", resume)).rejects.toThrow("crash before continuation ack"); for (let replay = 0; replay < 3; replay += 1) { await materializer.resumeAgent(task, "continuation-safe-id", resume); } expect(resumes).toBe(1); }); test("Canvas node effect completes only after its stamped project snapshot is durable", async () => { const originalWindow = (globalThis as { window?: unknown }).window; const originalGetItem = localforage.getItem.bind(localforage); const originalSetItem = localforage.setItem.bind(localforage); const indexedValues = new Map(); const persistedPayloads: string[] = []; let releasePersistence!: () => void; const persistenceGate = new Promise((resolve) => { releasePersistence = resolve; }); let persistenceStartedResolve!: () => void; const persistenceStarted = new Promise((resolve) => { persistenceStartedResolve = resolve; }); const localStorageValues = new Map(); Object.defineProperty(globalThis, "window", { configurable: true, value: { localStorage: { getItem: (key: string) => localStorageValues.get(key) ?? null, setItem: (key: string, value: string) => localStorageValues.set(key, value), removeItem: (key: string) => localStorageValues.delete(key), }, }, }); localforage.getItem = (async (key: string) => indexedValues.get(key) ?? null) as typeof localforage.getItem; localforage.setItem = (async (key: string, value: string) => { if (key.includes("infinite-canvas:canvas_store")) { persistedPayloads.push(value); persistenceStartedResolve(); await persistenceGate; } indexedValues.set(key, value); return value; }) as typeof localforage.setItem; const previousProjects = useCanvasStore.getState().projects; const previousAssets = useAssetStore.getState().assets; const node: CanvasNodeData = { id: "node-durable-barrier", type: CanvasNodeType.Image, title: "result", position: { x: 0, y: 0 }, width: 320, height: 180, metadata: { taskId: "backend-node-durable-barrier", status: "loading" }, }; const task: GenerationTask = { id: "backend-node-durable-barrier", provider: "remote-image-provider", type: "canvas_image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "READY", resultJson: JSON.stringify({ images: [{ dataUrl: "opaque://durable-node", width: 1, height: 1, mimeType: "image/png" }] }), outputs: [{ outputIndex: 0, mediaType: "image", materializedAssetId: "asset-durable-node" }], createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; useAssetStore.getState().replaceAssets([ { id: "asset-durable-node", kind: "image", title: "result", coverUrl: "opaque://durable-node", tags: [], metadata: {}, data: { dataUrl: "opaque://durable-node", storageKey: "resource:durable-node", width: 1, height: 1, bytes: 1, mimeType: "image/png" }, createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }, ]); useCanvasStore.setState({ projects: [ { id: "canvas-durable-barrier", title: "canvas", createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", nodes: [node], connections: [], chatSessions: [], activeChatId: null, backgroundMode: "dots", showImageInfo: false, viewport: { x: 0, y: 0, k: 1 }, directorScenes: [], }, ], }); let visibleNodes = [node]; let completeCalls = 0; let completeResolve!: () => void; const completed = new Promise((resolve) => { completeResolve = resolve; }); const baseEffects = createEffectStore(); const effects: GenerationTaskEffectStore = { ...baseEffects, async complete(effectKey, taskId, result) { completeCalls += 1; await baseEffects.complete(effectKey, taskId, result); completeResolve(); }, }; const materializer = createGenerationTaskMaterializer({ effects, async materializeOutput() { throw new Error("already materialized"); }, }); const effectKey = `attach-node:${task.id}:${node.id}:0`; try { const run = materializer.attachNode(task, node.id, 0, async ({ output }) => { await applyCanvasGenerationTaskNodeEffect({ projectId: "canvas-durable-barrier", nodeId: node.id, task, output: output!, effectKey, nodesRef: { get current() { return visibleNodes; }, set current(value) { visibleNodes = value; }, }, setNodes: (value) => { visibleNodes = typeof value === "function" ? value(visibleNodes) : value; }, }); }); expect(await Promise.race([persistenceStarted.then(() => "persistence"), completed.then(() => "complete")])).toBe("persistence"); expect(completeCalls).toBe(0); releasePersistence(); await run; expect(completeCalls).toBe(1); expect(persistedPayloads.at(-1)).toContain(effectKey); const restarted = JSON.parse(persistedPayloads.at(-1)!) as { state: { projects: Array<{ nodes: CanvasNodeData[] }> } }; expect(restarted.state.projects[0]?.nodes[0]?.metadata?.generationEffectKeys).toEqual([effectKey]); } finally { releasePersistence(); withCanvasStorePersistenceSuppressed(() => { useCanvasStore.setState({ projects: previousProjects }); }); useAssetStore.getState().replaceAssets(previousAssets); await flushAssetStorePersistence(); localforage.getItem = originalGetItem; localforage.setItem = originalSetItem; if (originalWindow === undefined) delete (globalThis as { window?: unknown }).window; else Object.defineProperty(globalThis, "window", { configurable: true, value: originalWindow }); } }); test("Canvas first durable write failure releases the claim and retry persists the effect before completion", async () => { const originalWindow = (globalThis as { window?: unknown }).window; const originalGetItem = localforage.getItem.bind(localforage); const originalSetItem = localforage.setItem.bind(localforage); const previousProjects = useCanvasStore.getState().projects; const previousAssets = useAssetStore.getState().assets; const scope = "canvas-first-write-retry"; const storageKey = `infinite-canvas:canvas_store:user:${scope}`; const values = new Map(); let failCanvasWrite = true; const localStorageValues = new Map(); Object.defineProperty(globalThis, "window", { configurable: true, value: { localStorage: { getItem: (key: string) => localStorageValues.get(key) ?? null, setItem: (key: string, value: string) => localStorageValues.set(key, value), removeItem: (key: string) => localStorageValues.delete(key), }, }, }); localforage.getItem = (async (key: string) => values.get(key) ?? null) as typeof localforage.getItem; localforage.setItem = (async (key: string, value: string) => { if (key === storageKey && failCanvasWrite) throw new Error("canvas first durable write failed"); values.set(key, value); return value; }) as typeof localforage.setItem; const node: CanvasNodeData = { id: "node-first-write-retry", type: CanvasNodeType.Image, title: "result", position: { x: 0, y: 0 }, width: 320, height: 180, metadata: { taskId: "backend-first-write-retry", status: "loading" }, }; const project = { id: "canvas-first-write-retry", title: "canvas", createdAt: "2026-08-14T00:00:00.000Z", updatedAt: "2026-08-14T00:00:00.000Z", nodes: [node], connections: [], chatSessions: [], activeChatId: null, backgroundMode: "dots" as const, showImageInfo: false, viewport: { x: 0, y: 0, k: 1 }, directorScenes: [], }; const task: GenerationTask = { id: "backend-first-write-retry", provider: "remote-image-provider", type: "canvas_image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "READY", resultJson: JSON.stringify({ images: [{ dataUrl: "https://example.invalid/retry-node.png", storageKey: "resource:retry-node", width: 1, height: 1, mimeType: "image/png" }] }), outputs: [{ outputIndex: 0, mediaType: "image", materializedAssetId: "asset-first-write-retry" }], createdAt: "2026-08-14T00:00:00.000Z", updatedAt: "2026-08-14T00:00:00.000Z", }; const effectKey = `attach-node:${task.id}:${node.id}:0`; values.set(storageKey, JSON.stringify({ state: { projects: [project] }, version: 0, storageRevision: 1 })); setActiveUserScope(scope); withCanvasStorePersistenceSuppressed(() => useCanvasStore.setState({ projects: [project] })); useAssetStore.getState().replaceAssets([ { id: "asset-first-write-retry", kind: "image", title: "result", coverUrl: "https://example.invalid/retry-node.png", tags: [], metadata: {}, data: { dataUrl: "https://example.invalid/retry-node.png", storageKey: "resource:retry-node", width: 1, height: 1, bytes: 1, mimeType: "image/png" }, createdAt: "2026-08-14T00:00:00.000Z", updatedAt: "2026-08-14T00:00:00.000Z", }, ]); let visibleNodes = [node]; let releases = 0; let completes = 0; const baseEffects = createEffectStore(); const effects: GenerationTaskEffectStore = { ...baseEffects, async complete(effect, taskId, result, binding) { completes += 1; await baseEffects.complete(effect, taskId, result, binding); }, async release(effect, taskId, binding) { releases += 1; await baseEffects.release(effect, taskId, binding); }, }; const materializer = createGenerationTaskMaterializer({ effects, materializeOutput: async () => ({}) }); const apply = () => materializer.attachNode(task, node.id, 0, async ({ output }) => { await applyCanvasGenerationTaskNodeEffect({ projectId: project.id, nodeId: node.id, task, output: output!, effectKey, nodesRef: { get current() { return visibleNodes; }, set current(value) { visibleNodes = value; }, }, setNodes: (value) => { visibleNodes = typeof value === "function" ? value(visibleNodes) : value; }, }); }); try { await expect(apply()).rejects.toThrow("canvas first durable write failed"); expect(releases).toBe(1); expect(completes).toBe(0); expect(generationEffectApplied(visibleNodes[0]?.metadata || {}, effectKey)).toBe(false); expect(values.get(storageKey)).not.toContain(effectKey); failCanvasWrite = false; await apply(); expect(completes).toBe(1); const durable = JSON.parse(values.get(storageKey)!) as { state: { projects: Array<{ nodes: CanvasNodeData[] }> } }; expect(generationEffectApplied(durable.state.projects[0]?.nodes[0]?.metadata || {}, effectKey)).toBe(true); withCanvasStorePersistenceSuppressed(() => useCanvasStore.setState({ projects: durable.state.projects as never })); expect(generationEffectApplied(useCanvasStore.getState().projects[0]?.nodes[0]?.metadata || {}, effectKey)).toBe(true); } finally { failCanvasWrite = false; withCanvasStorePersistenceSuppressed(() => useCanvasStore.setState({ projects: previousProjects })); useAssetStore.getState().replaceAssets(previousAssets); await flushAssetStorePersistence(); localforage.getItem = originalGetItem; localforage.setItem = originalSetItem; if (originalWindow === undefined) delete (globalThis as { window?: unknown }).window; else Object.defineProperty(globalThis, "window", { configurable: true, value: originalWindow }); } }); test("Canvas node effect keeps a newer mounted ordinary edit when the post-commit ordinary flush fails", async () => { const originalWindow = (globalThis as { window?: unknown }).window; const originalGetItem = localforage.getItem.bind(localforage); const originalSetItem = localforage.setItem.bind(localforage); const previousScope = getActiveUserScope(); const previousProjects = useCanvasStore.getState().projects; const previousAssets = useAssetStore.getState().assets; const scope = "canvas-node-post-commit-mounted-edit"; const storageKey = `infinite-canvas:canvas_store:user:${scope}`; const values = new Map(); const localStorageValues = new Map(); Object.defineProperty(globalThis, "window", { configurable: true, value: { localStorage: { getItem: (key: string) => localStorageValues.get(key) ?? null, setItem: (key: string, value: string) => localStorageValues.set(key, value), removeItem: (key: string) => localStorageValues.delete(key), }, }, }); const node: CanvasNodeData = { id: "node-post-commit-mounted-edit", type: CanvasNodeType.Image, title: "base node", position: { x: 0, y: 0 }, width: 320, height: 180, metadata: { taskId: "backend-post-commit-mounted-edit", status: "loading" }, }; const project = { id: "canvas-node-post-commit-mounted-edit", title: "canvas", createdAt: "2026-08-14T00:00:00.000Z", updatedAt: "2026-08-14T00:00:00.000Z", nodes: [node], connections: [], chatSessions: [], activeChatId: null, backgroundMode: "dots" as const, showImageInfo: false, viewport: { x: 0, y: 0, k: 1 }, directorScenes: [], }; const task: GenerationTask = { id: "backend-post-commit-mounted-edit", provider: "remote-image-provider", type: "canvas_image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "READY", resultJson: JSON.stringify({ images: [{ dataUrl: "https://example.invalid/post-commit.png", storageKey: "resource:post-commit", width: 1, height: 1, mimeType: "image/png" }] }), outputs: [{ outputIndex: 0, mediaType: "image", materializedAssetId: "asset-post-commit-mounted-edit" }], createdAt: "2026-08-14T00:00:00.000Z", updatedAt: "2026-08-14T00:00:00.000Z", }; const effectKey = `attach-node:${task.id}:${node.id}:0`; let visibleNodes = [node]; let generationCommitted = false; let failOrdinaryWrite = true; localforage.getItem = (async (key: string) => values.get(key) ?? null) as typeof localforage.getItem; localforage.setItem = (async (key: string, value: string) => { if (key === storageKey && !generationCommitted && value.includes(effectKey)) { values.set(key, value); generationCommitted = true; const committed = JSON.parse(value) as { state: { projects: Array<{ nodes: CanvasNodeData[] }> } }; const committedNode = committed.state.projects[0]!.nodes[0]!; visibleNodes = [{ ...committedNode, title: "newer ordinary node edit", position: { x: 88, y: 44 }, metadata: { ...committedNode.metadata, ordinaryNote: "newer" } }]; useCanvasStore.getState().updateProject(project.id, { nodes: visibleNodes }); return value; } if (key === storageKey && generationCommitted && failOrdinaryWrite) { failOrdinaryWrite = false; throw new Error("post-commit ordinary flush failed"); } values.set(key, value); return value; }) as typeof localforage.setItem; try { setActiveUserScope(scope); withCanvasStorePersistenceSuppressed(() => useCanvasStore.setState({ projects: [project] })); values.set(storageKey, JSON.stringify({ state: { projects: [project] }, version: 0, storageRevision: 1, tombstones: { projects: {}, nodes: {}, connections: {}, sessions: {}, messages: {} } })); useAssetStore.getState().replaceAssets([ { id: "asset-post-commit-mounted-edit", kind: "image", title: "result", coverUrl: "https://example.invalid/post-commit.png", tags: [], metadata: {}, data: { dataUrl: "https://example.invalid/post-commit.png", storageKey: "resource:post-commit", width: 1, height: 1, bytes: 1, mimeType: "image/png" }, createdAt: "2026-08-14T00:00:00.000Z", updatedAt: "2026-08-14T00:00:00.000Z", }, ]); await applyCanvasGenerationTaskNodeEffect({ projectId: project.id, nodeId: node.id, task, output: task.outputs![0]!, effectKey, nodesRef: { get current() { return visibleNodes; }, set current(value) { visibleNodes = value; }, }, setNodes: (value) => { visibleNodes = typeof value === "function" ? value(visibleNodes) : value; }, }); expect(generationCommitted).toBe(true); expect(visibleNodes[0]?.title).toBe("newer ordinary node edit"); expect(visibleNodes[0]?.position).toEqual({ x: 88, y: 44 }); expect(visibleNodes[0]?.metadata?.ordinaryNote).toBe("newer"); expect(generationEffectApplied(visibleNodes[0]?.metadata || {}, effectKey)).toBe(true); expect((JSON.parse(values.get(storageKey)!) as { state: { projects: Array<{ nodes: CanvasNodeData[] }> } }).state.projects[0]?.nodes[0]?.metadata?.generationEffectKeys).toContain(effectKey); await flushCanvasStorePersistence(); const retried = JSON.parse(values.get(storageKey)!) as { state: { projects: Array<{ nodes: CanvasNodeData[] }> } }; expect(retried.state.projects[0]?.nodes[0]?.title).toBe("newer ordinary node edit"); expect(retried.state.projects[0]?.nodes[0]?.metadata?.generationEffectKeys).toContain(effectKey); } finally { failOrdinaryWrite = false; setActiveUserScope(previousScope); withCanvasStorePersistenceSuppressed(() => useCanvasStore.setState({ projects: previousProjects })); useAssetStore.getState().replaceAssets(previousAssets); await flushAssetStorePersistence(); localforage.getItem = originalGetItem; localforage.setItem = originalSetItem; if (originalWindow === undefined) delete (globalThis as { window?: unknown }).window; else Object.defineProperty(globalThis, "window", { configurable: true, value: originalWindow }); } }); test("Canvas Agent continuation keeps a newer mounted ordinary edit when the post-commit ordinary flush fails", async () => { const originalWindow = (globalThis as { window?: unknown }).window; const originalGetItem = localforage.getItem.bind(localforage); const originalSetItem = localforage.setItem.bind(localforage); const previousScope = getActiveUserScope(); const previousProjects = useCanvasStore.getState().projects; const scope = "canvas-agent-post-commit-mounted-edit"; const storageKey = `infinite-canvas:canvas_store:user:${scope}`; const values = new Map(); const localStorageValues = new Map(); Object.defineProperty(globalThis, "window", { configurable: true, value: { localStorage: { getItem: (key: string) => localStorageValues.get(key) ?? null, setItem: (key: string, value: string) => localStorageValues.set(key, value), removeItem: (key: string) => localStorageValues.delete(key), }, }, }); const effectKey = "agent-resume:backend-agent-post-commit:continuation-post-commit"; const continuation = { id: "continuation-post-commit", taskId: "backend-agent-post-commit", status: "completed" as const, effectKey }; const node: CanvasNodeData = { id: "node-agent-post-commit", type: CanvasNodeType.Text, title: "agent base", position: { x: 0, y: 0 }, width: 320, height: 180, metadata: { content: "base", agentGenerationContinuation: { id: continuation.id, taskId: continuation.taskId, status: "pending" } }, }; const project = { id: "canvas-agent-post-commit-mounted-edit", title: "canvas", createdAt: "2026-08-14T00:00:00.000Z", updatedAt: "2026-08-14T00:00:00.000Z", nodes: [node], connections: [], chatSessions: [], activeChatId: null, backgroundMode: "dots" as const, showImageInfo: false, viewport: { x: 0, y: 0, k: 1 }, directorScenes: [], }; let visibleNodes = [node]; let generationCommitted = false; let failOrdinaryWrite = true; localforage.getItem = (async (key: string) => values.get(key) ?? null) as typeof localforage.getItem; localforage.setItem = (async (key: string, value: string) => { if (key === storageKey && !generationCommitted && value.includes(effectKey)) { values.set(key, value); generationCommitted = true; const committed = JSON.parse(value) as { state: { projects: Array<{ nodes: CanvasNodeData[] }> } }; const committedNode = committed.state.projects[0]!.nodes[0]!; visibleNodes = [{ ...committedNode, title: "newer ordinary agent edit", position: { x: 55, y: 66 }, metadata: { ...committedNode.metadata, ordinaryNote: "newer-agent" } }]; useCanvasStore.getState().updateProject(project.id, { nodes: visibleNodes }); return value; } if (key === storageKey && generationCommitted && failOrdinaryWrite) { failOrdinaryWrite = false; throw new Error("post-commit ordinary agent flush failed"); } values.set(key, value); return value; }) as typeof localforage.setItem; try { setActiveUserScope(scope); withCanvasStorePersistenceSuppressed(() => useCanvasStore.setState({ projects: [project] })); values.set(storageKey, JSON.stringify({ state: { projects: [project] }, version: 0, storageRevision: 1, tombstones: { projects: {}, nodes: {}, connections: {}, sessions: {}, messages: {} } })); await persistCanvasAgentGenerationContinuationEffect({ projectId: project.id, nodeId: node.id, continuation, effectKey, previousNodes: [node], nodesRef: { get current() { return visibleNodes; }, set current(value) { visibleNodes = value; }, }, setNodes: (value) => { visibleNodes = typeof value === "function" ? value(visibleNodes) : value; }, }); expect(generationCommitted).toBe(true); expect(visibleNodes[0]?.title).toBe("newer ordinary agent edit"); expect(visibleNodes[0]?.position).toEqual({ x: 55, y: 66 }); expect(visibleNodes[0]?.metadata?.ordinaryNote).toBe("newer-agent"); expect(visibleNodes[0]?.metadata?.agentGenerationContinuation).toMatchObject({ status: "completed", effectKey }); expect(generationEffectApplied(visibleNodes[0]?.metadata || {}, effectKey)).toBe(true); await flushCanvasStorePersistence(); const retried = JSON.parse(values.get(storageKey)!) as { state: { projects: Array<{ nodes: CanvasNodeData[] }> } }; expect(retried.state.projects[0]?.nodes[0]?.title).toBe("newer ordinary agent edit"); expect(retried.state.projects[0]?.nodes[0]?.metadata?.generationEffectKeys).toContain(effectKey); } finally { failOrdinaryWrite = false; setActiveUserScope(previousScope); withCanvasStorePersistenceSuppressed(() => useCanvasStore.setState({ projects: previousProjects })); localforage.getItem = originalGetItem; localforage.setItem = originalSetItem; if (originalWindow === undefined) delete (globalThis as { window?: unknown }).window; else Object.defineProperty(globalThis, "window", { configurable: true, value: originalWindow }); } }); test("Canvas agent continuation waits for its async completion callback before completing the effect", async () => { const task: GenerationTask = { id: "backend-agent-callback-ack", provider: "remote-cinematic-provider", type: "agent_storyboard_rows", status: "succeeded", prompt: "redacted", attempts: 1, createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; const continuation = { id: "continuation-callback-ack", taskId: task.id, status: "pending" as const, }; let completeCalls = 0; const baseEffects = createEffectStore(); const effects: GenerationTaskEffectStore = { ...baseEffects, async complete(effectKey, taskId, result) { completeCalls += 1; await baseEffects.complete(effectKey, taskId, result); }, }; const materializer = createGenerationTaskMaterializer({ effects, async materializeOutput() { throw new Error("no outputs"); }, }); let callbackStartedResolve!: () => void; const callbackStarted = new Promise((resolve) => { callbackStartedResolve = resolve; }); let releaseCallback!: () => void; const callbackGate = new Promise((resolve) => { releaseCallback = resolve; }); const run = consumeCanvasAgentGenerationContinuation( task, continuation, async () => { callbackStartedResolve(); await callbackGate; }, { consumeAgent: ((input, continuationId, consumer) => materializer.resumeAgent(input, continuationId, consumer)) as typeof consumeGenerationTaskAgent, }, ); await callbackStarted; await new Promise((resolve) => setImmediate(resolve)); expect(completeCalls).toBe(0); releaseCallback(); await run; expect(completeCalls).toBe(1); }); test("production Canvas agent continuation is durably completed before the materializer acknowledges the effect", async () => { const originalWindow = (globalThis as { window?: unknown }).window; const originalGetItem = localforage.getItem.bind(localforage); const originalSetItem = localforage.setItem.bind(localforage); const previousScope = getActiveUserScope(); const previousProjects = useCanvasStore.getState().projects; const values = new Map(); const localStorageValues = new Map(); let failDedicatedWrite = false; Object.defineProperty(globalThis, "window", { configurable: true, value: { localStorage: { getItem: (key: string) => localStorageValues.get(key) ?? null, setItem: (key: string, value: string) => localStorageValues.set(key, value), removeItem: (key: string) => localStorageValues.delete(key), }, }, }); localforage.getItem = (async (key: string) => values.get(key) ?? null) as typeof localforage.getItem; localforage.setItem = (async (key: string, value: string) => { if (failDedicatedWrite && key.includes("infinite-canvas:canvas_store") && value.includes("agent-resume:backend-agent-production-durable:continuation-agent-production-durable")) { throw new Error("continuation durable write failed"); } values.set(key, value); return value; }) as typeof localforage.setItem; const scope = "canvas-agent-production-durable"; const storageKey = `infinite-canvas:canvas_store:user:${scope}`; const projectId = "canvas-agent-production-durable"; const nodeId = "node-agent-production-durable"; const task: GenerationTask = { id: "backend-agent-production-durable", provider: "remote-cinematic-provider", type: "agent_storyboard_rows", status: "succeeded", prompt: "redacted", attempts: 1, createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; const continuation = { id: "continuation-agent-production-durable", taskId: task.id, status: "pending" as const, }; const pendingNode: CanvasNodeData = { id: nodeId, type: CanvasNodeType.Text, title: "agent node", position: { x: 0, y: 0 }, width: 320, height: 180, metadata: { content: "base", agentGenerationContinuation: continuation }, }; const project = { id: projectId, title: "canvas", createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", nodes: [pendingNode], connections: [], chatSessions: [], activeChatId: null, backgroundMode: "dots" as const, showImageInfo: false, viewport: { x: 0, y: 0, k: 1 }, directorScenes: [], }; let completeCalls = 0; const baseEffects = createEffectStore(); const effects: GenerationTaskEffectStore = { ...baseEffects, async complete(effectKey, taskId, result) { completeCalls += 1; await baseEffects.complete(effectKey, taskId, result); }, }; const materializer = createGenerationTaskMaterializer({ effects, async materializeOutput() { throw new Error("no outputs"); }, }); const nodesRef = { current: [pendingNode] }; let completionCallbacks = 0; const consume = () => consumeCanvasAgentGenerationContinuation( task, continuation, (nextContinuation) => { completionCallbacks += 1; const nextNodes = nodesRef.current.map((node) => node.id === nodeId ? { ...node, metadata: { ...node.metadata, agentGenerationContinuation: nextContinuation, ...(nextContinuation.effectKey ? { generationEffectKeys: [nextContinuation.effectKey] } : {}), }, } : node, ); nodesRef.current = nextNodes; useCanvasStore.getState().updateProject(projectId, { nodes: nextNodes }); }, { consumeAgent: ((input, continuationId, consumer) => materializer.resumeAgent(input, continuationId, consumer)) as typeof consumeGenerationTaskAgent, projectId, nodeId, nodesRef, setNodes: (value: CanvasNodeData[] | ((current: CanvasNodeData[]) => CanvasNodeData[])) => { nodesRef.current = typeof value === "function" ? value(nodesRef.current) : value; }, } as never, ); try { setActiveUserScope(scope); useCanvasStore.setState({ projects: [project] }); await flushCanvasStorePersistence(); failDedicatedWrite = true; await expect(consume()).rejects.toThrow("continuation durable write failed"); expect(completeCalls).toBe(0); expect(completionCallbacks).toBe(0); expect((JSON.parse(values.get(storageKey)!) as { state: { projects: Array<{ nodes: CanvasNodeData[] }> } }).state.projects[0]?.nodes[0]?.metadata?.agentGenerationContinuation?.status).toBe("pending"); failDedicatedWrite = false; await consume(); await flushCanvasStorePersistence(); await consume(); await consume(); const effectKey = `agent-resume:${task.id}:${continuation.id}`; const durable = JSON.parse(values.get(storageKey)!) as { state: { projects: Array<{ nodes: CanvasNodeData[] }> } }; const durableNode = durable.state.projects[0]?.nodes[0]; const liveNode = useCanvasStore.getState().projects[0]?.nodes[0]; expect(completeCalls).toBe(1); expect(completionCallbacks).toBe(1); expect(durableNode?.metadata?.agentGenerationContinuation).toMatchObject({ status: "completed", effectKey }); expect(durableNode?.metadata?.generationEffectKeys).toEqual([effectKey]); expect(liveNode?.metadata?.agentGenerationContinuation).toMatchObject({ status: "completed", effectKey }); } finally { setActiveUserScope(previousScope); withCanvasStorePersistenceSuppressed(() => useCanvasStore.setState({ projects: previousProjects })); localforage.getItem = originalGetItem; localforage.setItem = originalSetItem; if (originalWindow === undefined) delete (globalThis as { window?: unknown }).window; else Object.defineProperty(globalThis, "window", { configurable: true, value: originalWindow }); } }); test("refresh recovery keeps a failed local Canvas ack retryable and durably preserves storyboard plus continuation on replay", async () => { const originalWindow = (globalThis as { window?: unknown }).window; const originalGetItem = localforage.getItem.bind(localforage); const originalSetItem = localforage.setItem.bind(localforage); const previousProjects = useCanvasStore.getState().projects; const previousScope = getActiveUserScope(); const scope = "canvas-refresh-storyboard-ack-retry"; const storageKey = `infinite-canvas:canvas_store:user:${scope}`; const values = new Map(); const localStorageValues = new Map(); let failCanvasWrite = false; Object.defineProperty(globalThis, "window", { configurable: true, value: { localStorage: { getItem: (key: string) => localStorageValues.get(key) ?? null, setItem: (key: string, value: string) => localStorageValues.set(key, value), removeItem: (key: string) => localStorageValues.delete(key), }, }, }); localforage.getItem = (async (key: string) => values.get(key) ?? null) as typeof localforage.getItem; localforage.setItem = (async (key: string, value: string) => { if (key === storageKey && failCanvasWrite) throw new Error("canvas continuation durable ack failed"); values.set(key, value); return value; }) as typeof localforage.setItem; const task: GenerationTask = { id: "backend-refresh-storyboard-ack-retry", projectId: "canvas-refresh-storyboard-ack-retry", provider: "remote-cinematic-provider", type: "agent_storyboard_rows", status: "succeeded", prompt: "redacted", attempts: 1, resultJson: JSON.stringify({ title: "Recovered storyboard", rows: [{ plotDescription: "Recovered shot", dialogue: "Hello" }] }), createdAt: "2026-08-14T00:00:00.000Z", updatedAt: "2026-08-14T00:01:00.000Z", }; const continuation = { id: "continuation-refresh-storyboard-ack-retry", taskId: task.id, status: "pending" as const }; const pendingNode: CanvasNodeData = { id: "node-refresh-storyboard-ack-retry", type: CanvasNodeType.Script, title: "Script", position: { x: 0, y: 0 }, width: 640, height: 480, metadata: { status: "loading", taskId: task.id, agentGenerationContinuation: continuation }, }; const project = { id: task.projectId!, title: "canvas", createdAt: "2026-08-14T00:00:00.000Z", updatedAt: "2026-08-14T00:00:00.000Z", nodes: [pendingNode], connections: [], chatSessions: [], activeChatId: null, backgroundMode: "dots" as const, showImageInfo: false, viewport: { x: 0, y: 0, k: 1 }, directorScenes: [], }; const baseEffects = createEffectStore(); let completeCalls = 0; const effects: GenerationTaskEffectStore = { ...baseEffects, async complete(effectKey, taskId, result, binding) { completeCalls += 1; await baseEffects.complete(effectKey, taskId, result, binding); }, }; const materializer = createGenerationTaskMaterializer({ effects, materializeOutput: async () => ({}) }); let visibleNodes = [pendingNode]; const nodesRef = { current: [pendingNode] }; const setNodes = (value: CanvasNodeData[] | ((current: CanvasNodeData[]) => CanvasNodeData[])) => { visibleNodes = typeof value === "function" ? value(visibleNodes) : value; }; const consumeContinuation = ( inputTask: GenerationTask, inputContinuation: typeof continuation, onCompleted: (next: typeof continuation & { effectKey?: string }, signal?: AbortSignal) => Promise | void, dependencies: Parameters[3], signal?: AbortSignal, ) => consumeCanvasAgentGenerationContinuation( inputTask, inputContinuation, onCompleted as Parameters[2], { ...dependencies, consumeAgent: ((materialized, continuationId, consumer) => materializer.resumeAgent(materialized, continuationId, consumer)) as NonNullable[3]>["consumeAgent"], }, signal, ); const recover = () => recoverCanvasGenerationTaskNode({ projectId: project.id, node: pendingNode, completed: task, continuationOnly: false, nodesRef, setNodes: setNodes as never, applyGenerationTaskResult: async () => undefined, signal: new AbortController().signal, consumeContinuation: consumeContinuation as never, }); try { setActiveUserScope(scope); withCanvasStorePersistenceSuppressed(() => useCanvasStore.setState({ projects: [project] })); values.set(storageKey, JSON.stringify({ state: { projects: [project] }, version: 0, storageRevision: 1, tombstones: { projects: {}, nodes: {}, connections: {}, sessions: {}, messages: {} } })); failCanvasWrite = true; await recover(); expect(completeCalls).toBe(0); expect(visibleNodes[0]?.metadata?.storyboard?.rows).toHaveLength(1); expect(visibleNodes[0]?.metadata?.agentGenerationContinuation?.status).toBe("pending"); expect(nodesRef.current[0]?.metadata?.storyboard?.rows).toHaveLength(1); expect(nodesRef.current[0]?.metadata?.agentGenerationContinuation?.status).toBe("pending"); failCanvasWrite = false; await recover(); await recover(); await recover(); const effectKey = `agent-resume:${task.id}:${continuation.id}`; const durable = JSON.parse(values.get(storageKey)!) as { state: { projects: Array<{ nodes: CanvasNodeData[] }> } }; const durableNode = durable.state.projects[0]?.nodes[0]; expect(completeCalls).toBe(1); expect(durableNode?.metadata?.storyboard?.rows).toHaveLength(1); expect(durableNode?.metadata?.storyboard?.rows[0]?.plotDescription).toBe("Recovered shot"); expect(durableNode?.metadata?.agentGenerationContinuation).toMatchObject({ status: "completed", effectKey }); expect(durableNode?.metadata?.generationEffectKeys).toEqual([effectKey]); expect(visibleNodes[0]?.metadata?.storyboard?.rows[0]?.plotDescription).toBe("Recovered shot"); expect(visibleNodes[0]?.metadata?.agentGenerationContinuation).toMatchObject({ status: "completed", effectKey }); } finally { failCanvasWrite = false; setActiveUserScope(previousScope); withCanvasStorePersistenceSuppressed(() => useCanvasStore.setState({ projects: previousProjects })); localforage.getItem = originalGetItem; localforage.setItem = originalSetItem; if (originalWindow === undefined) delete (globalThis as { window?: unknown }).window; else Object.defineProperty(globalThis, "window", { configurable: true, value: originalWindow }); } }); test("agent continuation acknowledgement waits for the durable stamped Canvas snapshot", async () => { const originalWindow = (globalThis as { window?: unknown }).window; const originalGetItem = localforage.getItem.bind(localforage); const originalSetItem = localforage.setItem.bind(localforage); const indexedValues = new Map(); const persistedPayloads: string[] = []; let releasePersistence!: () => void; const persistenceGate = new Promise((resolve) => { releasePersistence = resolve; }); let persistenceStartedResolve!: () => void; const persistenceStarted = new Promise((resolve) => { persistenceStartedResolve = resolve; }); const localStorageValues = new Map(); Object.defineProperty(globalThis, "window", { configurable: true, value: { localStorage: { getItem: (key: string) => localStorageValues.get(key) ?? null, setItem: (key: string, value: string) => localStorageValues.set(key, value), removeItem: (key: string) => localStorageValues.delete(key), }, }, }); localforage.getItem = (async (key: string) => indexedValues.get(key) ?? null) as typeof localforage.getItem; localforage.setItem = (async (key: string, value: string) => { if (key.includes("infinite-canvas:canvas_store")) { persistedPayloads.push(value); persistenceStartedResolve(); await persistenceGate; } indexedValues.set(key, value); return value; }) as typeof localforage.setItem; const previousProjects = useCanvasStore.getState().projects; const session = { id: "session-agent-durable", title: "agent", messages: [], createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; useCanvasStore.setState({ projects: [ { id: "canvas-agent-durable", title: "canvas", createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", nodes: [], connections: [], chatSessions: [session], activeChatId: session.id, backgroundMode: "dots", showImageInfo: false, viewport: { x: 0, y: 0, k: 1 }, directorScenes: [], }, ], }); const task: GenerationTask = { id: "backend-agent-durable", provider: "remote-cinematic-provider", type: "agent_storyboard_rows", status: "succeeded", prompt: "redacted", attempts: 1, createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; let completeCalls = 0; let completeResolve!: () => void; const completed = new Promise((resolve) => { completeResolve = resolve; }); const baseEffects = createEffectStore(); const effects: GenerationTaskEffectStore = { ...baseEffects, async complete(effectKey, taskId, result) { completeCalls += 1; await baseEffects.complete(effectKey, taskId, result); completeResolve(); }, }; const materializer = createGenerationTaskMaterializer({ effects, async materializeOutput() { throw new Error("no outputs"); }, }); const effectKey = `agent-resume:${task.id}:continuation-agent-durable`; let sideEffects = 0; const consume = () => materializer.resumeAgent(task, "continuation-agent-durable", async () => { const current = useCanvasStore.getState().projects.find((project) => project.id === "canvas-agent-durable")!; const currentSession = current.chatSessions[0]!; if (generationEffectApplied(currentSession, effectKey)) return; sideEffects += 1; const durableSession = applyGenerationConsumerEffect(currentSession, effectKey, (value) => value).value; const node: CanvasNodeData = { id: "agent-created-node", type: CanvasNodeType.Text, title: "agent result", position: { x: 0, y: 0 }, width: 320, height: 180, metadata: { content: "durable" }, }; await persistCanvasGenerationEffect({ projectId: current.id, effectKey, previousNodes: current.nodes, nodes: [...current.nodes, node], previousConnections: current.connections, connections: current.connections, previousChatSessions: current.chatSessions, chatSessions: [durableSession], previousActiveChatId: current.activeChatId, activeChatId: currentSession.id, }); }); try { const run = consume(); expect(await Promise.race([persistenceStarted.then(() => "persistence"), completed.then(() => "complete")])).toBe("persistence"); expect(completeCalls).toBe(0); releasePersistence(); await run; expect(completeCalls).toBe(1); const persisted = JSON.parse(persistedPayloads.at(-1)!) as { state: { projects: typeof previousProjects } }; useCanvasStore.setState({ projects: persisted.state.projects }); await consume(); await consume(); await consume(); const restarted = useCanvasStore.getState().projects[0]!; expect(sideEffects).toBe(1); expect(restarted.nodes.filter((node) => node.id === "agent-created-node")).toHaveLength(1); expect(restarted.chatSessions[0]?.generationEffectKeys).toEqual([effectKey]); } finally { releasePersistence(); withCanvasStorePersistenceSuppressed(() => { useCanvasStore.setState({ projects: previousProjects }); }); localforage.getItem = originalGetItem; localforage.setItem = originalSetItem; if (originalWindow === undefined) delete (globalThis as { window?: unknown }).window; else Object.defineProperty(globalThis, "window", { configurable: true, value: originalWindow }); } }); test("production consumer adapters preserve deterministic effect keys", async () => { const task: GenerationTask = { id: "task-production-effect-key", type: "image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "READY", outputs: [{ outputIndex: 0, mediaType: "image", materializedAssetId: "asset-stable-id" }], createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; const seen: string[] = []; await consumeGenerationTaskNode( task, "node-safe-id", 0, async ({ effectKey }) => { seen.push(effectKey); }, { materialize: async (input) => input, attachNode: async (input, _nodeId, _outputIndex, consumer) => { await consumer({ task: input, output: input.outputs?.[0], effectKey: "attach-node:task-production-effect-key:node-safe-id:0" }); return "applied"; }, }, ); await consumeGenerationTaskMessage( task, "message-safe-id", async ({ effectKey }) => { seen.push(effectKey); }, { materialize: async (input) => input, materializedUrls: () => ["opaque://materialized"], attachMessage: async (input, _messageId, _outputIndex, consumer) => { await consumer({ task: input, output: input.outputs?.[0], effectKey: "attach-message:task-production-effect-key:message-safe-id:0" }); return "applied"; }, }, ); await consumeGenerationTaskAgent( task, "continuation-safe-id", async ({ effectKey }) => { seen.push(effectKey); }, { resumeAgent: async (input, _continuationId, consumer) => { await consumer({ task: input, effectKey: "agent-resume:task-production-effect-key:continuation-safe-id" }); return "applied"; }, }, ); expect(seen).toEqual(["attach-node:task-production-effect-key:node-safe-id:0", "attach-message:task-production-effect-key:message-safe-id:0", "agent-resume:task-production-effect-key:continuation-safe-id"]); }); test("production consumer adapters forward one abort signal through materialization and attachment", async () => { const task: GenerationTask = { id: "task-production-abort-signal", type: "image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "READY", outputs: [{ outputIndex: 0, mediaType: "image", materializedAssetId: "asset-stable-id" }], createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; const controller = new AbortController(); const seen: Array = []; await consumeGenerationTaskNode(task, "node-abort", 0, async () => {}, { signal: controller.signal, materialize: async (input, signal) => { seen.push(signal); return input; }, attachNode: async (input, _nodeId, _outputIndex, consumer, signal) => { seen.push(signal); await consumer({ task: input, output: input.outputs?.[0], effectKey: "attach-node:abort" }); return "applied"; }, }); await consumeGenerationTaskMessage(task, "message-abort", async () => {}, { signal: controller.signal, materialize: async (input, signal) => { seen.push(signal); return input; }, materializedUrls: () => ["opaque://materialized"], attachMessage: async (input, _messageId, _outputIndex, consumer, signal) => { seen.push(signal); await consumer({ task: input, output: input.outputs?.[0], effectKey: "attach-message:abort" }); return "applied"; }, }); await consumeGenerationTaskAgent(task, "agent-abort", async () => {}, { signal: controller.signal, materialize: async (input, signal) => { seen.push(signal); return input; }, resumeAgent: async (input, _continuationId, consumer, signal) => { seen.push(signal); await consumer({ task: input, effectKey: "agent-resume:abort" }); return "applied"; }, }); expect(seen).toHaveLength(6); expect(seen.every((signal) => signal instanceof AbortSignal)).toBe(true); expect(seen[0]).toBe(seen[1]); expect(seen[2]).toBe(seen[3]); expect(seen[4]).toBe(seen[5]); }); test("production node consumer receives the attachment lease signal", async () => { const task: GenerationTask = { id: "task-production-lease-signal", type: "image", status: "succeeded", prompt: "redacted", attempts: 1, resultState: "READY", outputs: [{ outputIndex: 0, mediaType: "image", materializedAssetId: "asset-production-lease-signal" }], createdAt: "2026-08-14T00:00:00.000Z", updatedAt: "2026-08-14T00:00:00.000Z", }; const parentController = new AbortController(); const leaseController = new AbortController(); let consumerSignal: AbortSignal | undefined; await consumeGenerationTaskNode( task, "node-production-lease-signal", 0, async ({ signal }) => { consumerSignal = signal; }, { signal: parentController.signal, materialize: async (input) => input, attachNode: async (input, _nodeId, _outputIndex, consumer) => { await consumer({ task: input, output: input.outputs?.[0], effectKey: "attach-node:production-lease-signal", signal: leaseController.signal }); return "applied"; }, }, ); expect(consumerSignal).toBe(leaseController.signal); expect(consumerSignal).not.toBe(parentController.signal); }); test("Canvas asset sync freezes its account key and is aborted by account lifecycle cleanup", async () => { const source = await Bun.file(new URL("../src/services/project-asset-sync.ts", import.meta.url)).text(); expect(source).toContain("const scope = getActiveUserScope();"); expect(source).toContain("const key = [scope,"); expect(source).toContain("runGenerationConsumer(options.signal"); expect(source.match(/readImageMeta\(url, signal\)/g)).toHaveLength(2); }); test("generic task materialization never treats a local Canvas id as a backend project id", async () => { const source = await Bun.file(new URL("../src/services/project-asset-sync.ts", import.meta.url)).text(); expect(source).not.toContain("if (input.task.projectId) await syncAssetToProject(assetId, input.task.projectId"); expect(source).toContain("if (!options.domainProjectId) return"); expect(source).toContain("await syncAssetToProject(asset.id, options.domainProjectId"); }); test("production materialization forwards parent abort into its lease-owned output sink", async () => { const controller = new AbortController(); let sinkSignal: AbortSignal | undefined; let sinkStartedResolve!: () => void; const sinkStarted = new Promise((resolve) => { sinkStartedResolve = resolve; }); const materializer = createGenerationTaskMaterializer({ effects: createEffectStore(), async materializeOutput(input) { sinkSignal = input.signal; sinkStartedResolve(); await new Promise((resolve) => input.signal?.addEventListener("abort", () => resolve(), { once: true })); throw new DOMException("The operation was aborted", "AbortError"); }, }); const task: GenerationTask = { id: "task-materialize-signal", type: "image", status: "succeeded", prompt: "redacted", attempts: 1, outputs: [{ outputIndex: 0, mediaType: "image" }], createdAt: "2026-08-13T00:00:00.000Z", updatedAt: "2026-08-13T00:00:00.000Z", }; const materializing = materializer.materialize(task, controller.signal); await sinkStarted; expect(sinkSignal).toBeDefined(); expect(sinkSignal).not.toBe(controller.signal); controller.abort(); await expect(materializing).rejects.toMatchObject({ name: "AbortError" }); expect(sinkSignal?.aborted).toBe(true); }); });