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=/,