diff --git a/app/image/page.tsx b/app/image/page.tsx index d0ee7b96..5bcd4a3f 100644 --- a/app/image/page.tsx +++ b/app/image/page.tsx @@ -604,9 +604,23 @@ export default function ImageStudioPage() { ); } + /** + * AIT-576 residual chrome + AIT-622 leave-wait: owner deep-link or plain list. + * Lab / missing / non-private stay plain /library (never invent ?job=). + */ + const imageLibraryHref = libraryWorkbenchHandoffHref({ + demo, + privateResult: lastPrivateResult, + requestId: lastRequestId, + }); + const imageLibraryHandoffKind = imageLibraryHref.includes("job=") + ? "request-id" + : "list"; + /** * Stop waiting on Image Studio without aborting the original still POST or * inventing a refund restore. User can open Library while the same request finishes. + * AIT-622: owner-safe deep-link via imageLibraryHref (same helper as residual chrome). */ function leaveWaitingKeepBackground() { const plan = planGenerateWaitLeave("detach"); @@ -622,7 +636,8 @@ export default function ImageStudioPage() { setError(null); clearFailGate(); // Soft client navigation keeps the original fetch alive in this document. - router.push("/library"); + // Reuse residual chrome helper inputs (lastPrivateResult + lastRequestId). + router.push(imageLibraryHref); } async function generate(opts?: { prompt?: string; aspect?: string }) { @@ -814,16 +829,6 @@ export default function ImageStudioPage() { ), }); - /** AIT-576: residual Library chrome — owner deep-link or plain list. */ - const imageLibraryHref = libraryWorkbenchHandoffHref({ - demo, - privateResult: lastPrivateResult, - requestId: lastRequestId, - }); - const imageLibraryHandoffKind = imageLibraryHref.includes("job=") - ? "request-id" - : "list"; - return (
readFileSync(join(root, rel), "utf8"); assert.match(imagePage, /data-generate-leave="cancel"/); assert.match(imagePage, /data-image-leave="detach"/); assert.match(imagePage, /Open Library · keep generating/); - assert.match(imagePage, /router\.push\(["']\/library["']\)/); + // AIT-622: owner-safe Library deep-link via imageLibraryHref (not bare /library) + assert.match(imagePage, /router\.push\(\s*imageLibraryHref\s*\)/); + assert.match(imagePage, /imageLibraryHref/); // Detach function never aborts primary, cancels ledger, or invents restore. { const leaveFn = imagePage.match( @@ -553,7 +555,8 @@ const read = (rel) => readFileSync(join(root, rel), "utf8"); assert.match(leaveFn, /planGenerateWaitLeave\("detach"\)/); assert.match(leaveFn, /abortRef\.current = null/); assert.match(leaveFn, /detachedWaitRef\.current = true/); - assert.match(leaveFn, /router\.push\(["']\/library["']\)/); + assert.match(leaveFn, /router\.push\(\s*imageLibraryHref\s*\)/); + assert.doesNotMatch(leaveFn, /router\.push\(\s*["']\/library["']\s*\)/); assert.doesNotMatch(leaveFn, /\.abort\s*\(/); assert.doesNotMatch(leaveFn, /cancelSessionStill|DELETE/); assert.doesNotMatch(leaveFn, /setFailCreditState/); diff --git a/scripts/library-workbench-deeplink-smoke.mjs b/scripts/library-workbench-deeplink-smoke.mjs index 3e26a874..8c6abb01 100644 --- a/scripts/library-workbench-deeplink-smoke.mjs +++ b/scripts/library-workbench-deeplink-smoke.mjs @@ -1,8 +1,9 @@ #!/usr/bin/env node /** - * AIT-529 / AIT-392 / AIT-546 / AIT-568 / AIT-576 / AIT-558: Workbench + + * AIT-529 / AIT-392 / AIT-546 / AIT-568 / AIT-576 / AIT-558 / AIT-622: Workbench + * AfterPath + WaitStage + residual CreateStudio chips + Image residual Library - * chrome + BatchStudio/Seller Pack done → requestId deep-link (owner-safe). + * chrome + Image leave-wait keep-background + BatchStudio/Seller Pack done → + * requestId deep-link (owner-safe). * * Source + pure-logic contract: * - libraryWorkbenchHandoffHref only deep-links live private + UUID requestId @@ -14,6 +15,7 @@ * - GenerateAfterPath Library chips use helper (fail-closed) * - GenerateWaitStage leave exposes libraryHref / handoff markers * - Image Studio residual Library chrome uses helper; Lab/missing fail-closed + * - Image leaveWaitingKeepBackground reuses imageLibraryHref (not bare /library) * - BatchStudio / Seller Pack post-pack Library CTAs reuse same helper * - LibraryGrid matches deep-link by id or requestId; missing/foreign → not-your-toy * @@ -360,6 +362,35 @@ assert.match( /href=\{imageLibraryHref\}/, "Image residual Library chrome must bind imageLibraryHref" ); +assert.match( + imageStudio, + /router\.push\(\s*imageLibraryHref\s*\)/, + "Image leave-wait keep-background must hand off owner-safe Library href" +); +{ + const leaveFn = imageStudio.match( + /function leaveWaitingKeepBackground\(\) \{[\s\S]*?\n \}/ + )?.[0]; + assert.ok( + leaveFn, + "leaveWaitingKeepBackground must exist on Image Studio" + ); + assert.match( + leaveFn, + /router\.push\(\s*imageLibraryHref\s*\)/, + "leaveWaitingKeepBackground must push imageLibraryHref (not bare /library)" + ); + assert.doesNotMatch( + leaveFn, + /router\.push\(\s*["']\/library["']\s*\)/, + "leaveWaitingKeepBackground must not hardcode plain /library" + ); + assert.doesNotMatch( + leaveFn, + /\/library\?job=/, + "leaveWaitingKeepBackground must never invent hardcoded ?job=" + ); +} assert.match( imageStudio, /data-library-handoff=/,