Repository navigation
feat: restore legacy recall, deletion, queue and workflow-form behaviors - #9803
Conversation
…erences and reinstalled models Guidance-family records restore their guidance key; Krea-2 seed variance values recall with the rebalance. An explicitly empty reference list clears the current references instead of leaving them active. Main, component and VAE references resolve by install key, then recorded hash, then name and base.
…ails shortcut Gallery deletion scans the project for the selected media and always confirms when a surface still uses it, listing the affected surfaces. Videos gain a Load Workflow action backed by the record's has_workflow flag. The Preview registers I to toggle the details popover.
… nodes Form fields render their authored dropdown, slider and bounds; validation and shuffle use the narrowed bounds, and the form builder edits them. Form text and field descriptions turn http(s) URLs into new-tab links. The editor toolbar arranges all or selected nodes as one undoable step and fits the result.
…ory pruning and execution diagnostics Invoke Front now sends prepend=true through every submission source; ordinary Invoke still appends. Queue rows cancel waiting workflow-call parents and retry failed or canceled roots; expanded rows cancel their batch and View JSON reads the saved session graph, results and workflow on demand. The header menu clears finished history, administrators manage the invocation cache beneath the queue and set max_queue_history from Settings → Server; the mock backend gains the matching endpoints.
…and videos Load Workflow for a video and for an image now go through one loader keyed by media kind. The W shortcut loads the workflow of a selected video as well as a selected image.
Each connected component is laid out on its own and components fill wrapped rows, so Arrange nodes no longer stacks isolated nodes into one tall column.
… bounds editable Invoke readiness and builder markers validate against the form-narrowed template, an inverted min/max pair is kept as authored and ignored with a warning, and integer fields reject fractional steps. Scoped Clear Finished deletes terminal root items only, and retry/cancel-batch are account-fenced.
The media workflow loader lives in the recall module again, and the slider controls join the editor boot chunk; each had become its own startup request.
…ngs and modules Request counts and chunk sets are unchanged; the growth is the new user-facing strings and the form-settings, media-usage and queue-detail modules joining their routes' existing chunks.
The field binding derives the narrowed template directly; the compiler memoizes it.
The slider now runs between the first and last multiples of the step inside the bounds, so bounds of 1–100 with a step of 8 offer 8…96 instead of stops validation rejects; shuffle draws from the same bounds. Multiples are judged with a tolerance relative to the quotient so fine authored steps far from zero stay valid, and the form builder warns when the bounds admit no stop.
Pfannkuchensack
left a comment
There was a problem hiding this comment.
Review of f5ba648. f5ba648 resolves my slider finding: the track now ends on step multiples. Everything below still applies at that head.
Major
1. Retried items produce output nothing in webv2 picks up.
RetryQueueItemButton.tsx:27 calls retry_items_by_id. The backend clones the root under a new item id and keeps the root's destination, origin and project_id. Nothing in webv2 tracks that new id:
- The coordinator routes results only for backend ids from its own runs. Untracked terminal events are only buffered (
coordinator.ts:809). - The gallery invalidates only on
image_uploaded/video_uploadedor on tracked completions.
The result:
- A retried canvas item never reaches staging; its output is intermediate, so it never shows up in the gallery either.
- A retried gallery item does not refresh the gallery live.
The user sees "Retry queued" and the GPU runs, but the result never appears where the original was headed. webv1 staging adopts canvas-destination items, so this is a parity gap.
Options:
- Adopt retried items into the Workbench run lifecycle, e.g. via
retried_from_item_id. - At minimum, refresh the gallery when retried items complete, and hide Retry for
destination === 'canvas'(queueOwnership.ts:24).
Minor
2. Invoke Front loses prepend when a run is restored.
normalizeRestoredQueueItem rebuilds snapshot from an allowlist that omits prepend (queueRunRestoration.ts:135). If the page reloads or the backend disconnects before the enqueue is admitted, the replayed submission appends instead of prepending. Suggest allowlisting prepend: true and adding a journal round-trip test.
Related: queue state is not part of the project document at all, so nothing strips prepend from exports. The compatibility note in the description should say that instead.
3. The invocation cache footer offers Enable/Disable when node_cache_size is 0.
In that case the backend's enable(), disable() and clear() return early, and status reports enabled: false. The footer shows "Disabled" with an Enable button. Clicking it toasts "Invocation cache enabled", and the status stays Disabled (InvocationCacheFooter.tsx:57). webv1 disables these controls when max_size === 0 (invocationCacheControls.ts).
4. An authored bound outside the template's range empties the range.
applyNodeFieldFormSettings compares the authored min only with template.minimum, never with template.maximum (and the max likewise) (formFieldSettings.ts:198). Example: template le=10, authored min 20. The narrowed template is {min 20, max 10}, every value fails validation, and Invoke is blocked.
f5ba648 now warns for slider/combined controls. The number-input component still gets no warning. This contradicts the docstring ("a bound applies only inside the template's own range"). Suggest ignoring such bounds the way inverted pairs are ignored.
5. Model recall never falls back from hash to name+base.
resolveRecordedModel goes key → hash, and uses name+base only when no hash was recorded (imageRecall.ts:554). The description says key → hash → name+base, and webv1's resolveModel does fall through.
The hash differs after a reinstall in these cases:
hashing_algorithm: random- a changed hashing algorithm
- a re-converted model
webv2 then drops the main model, VAE and components where webv1 restores them. If skipping the fallback is intentional, the description should say so.
6. The Queue history editor shows an unknown value as "Keep all".
- If the admin's
runtime_configread fails, the store holdsmaxQueueHistory: undefined. The editor renders that as an empty, editable field with the "Keep all" placeholder (MaxQueueHistorySettings.tsx:24). That is exactly the null/undefined distinction the store was built to keep. ?? valueafter the PATCH shows a servernullas the requested value (generationDevicesStore.ts:165).- If the unrelated
generation_device_optionsrequest fails, this editor is hidden behind "Failed to load generation devices".
7. The mock retry returns the wrong ids, and the fixture test asserts that contract.
The real retry_items_by_id returns the ids of the retried roots. The mock returns the new clone ids (mock-backend.mjs:1298), and mock-backend-fixtures.test.mjs:960 fetches retried_item_ids[0] as the pending clone.
Two more divergences:
- The mock's
retried_from_item_iddoes not chain to the original item. - The mock's prune deletes finished children of still-active roots; the backend's prunable filter excludes them.
There is no runtime bug today, because the UI only reads .length. But the next feature built on these ids will pass against the mock and fail against the server.
8. Docs.
06.Gallery/introduction.mdx:153still says deletion does not check whether media is in use. Line 151 says "Don't ask me again" stops all confirmations.04.Image Generation/01.introduction.mdx:52: "the batch still waits its turn behind other users' jobs" holds only inround_robin. In multi-user FIFO, prepend priority is the queue-wide top pending priority + 1, and_FIFO_NEXTorders by priority first, so the batch jumps ahead of everyone.
9. The performance re-record changes pinned source-owner sets, not just bytes.
- The Launchpad initial graph gains
platform/ui/SliderNumberField.tsx(viaROUTE_SHARED_MODULES,vite.config.mts:59). - The editor graph gains
workflow/core/formFieldSettings.ts. - Several browser routes gain the new queue/form/media-usage modules.
- Costs: Launchpad initial +6 KB raw, editor initial +18 KB raw.
The trade-off looks reasonable; request counts are unchanged. ARCHITECTURE.md asks for owner-set changes and their cost to be stated, but the description says "chunk sets are unchanged".
10. The direct useEffect in ImageContextMenu gained a backend fetch.
ImageContextMenu.tsx:342 now Promise.alls recall capabilities with hasVideoWorkflow. That is an uncached record fetch with no abort signal. A failure of either request also clears the other's result. webv2 AGENTS.md asks to migrate effects whose behavior changes. A Query-backed read, or has_workflow on the listing, would fit better.
11. Small efficiency issues.
queueKeys.itemDetailsits under['queue'](queries.ts:16). An open View JSON dialog therefore refetches the full item, graph and results on every queue event, even for terminal items.- Scoped Clear Finished hydrates every item in scope through
items_by_idsto read onlystatusandparent_item_id(serverApi.ts:159). The summaries endpoint carries both fields. Clear Failed has the same pattern already.
Test gaps
-
Invoke Front: the reducer test dispatches
submitInvocationSnapshot(workbenchState.test.ts:441), but production only dispatchessubmitResolvedInvocationSnapshotandsubmitCanvasInvocationSnapshot. These forwarding sites are untested:workbenchState.ts:4847and:5407invocationSubmit.tsprepareCanvasInvocation.ts- the workflow branch in
runtime.ts
Dropping
prependat any of them would stay green.invocationSubmit.test.tsandprepareCanvasInvocation.test.tsalready have harnesses that can assert it. -
Max queue history: blank →
null(keep all) vs0(prune all) is untested.Number('') === 0makes this an easy regression with destructive consequences. -
No coverage at all:
InvocationCacheFooter(admin gate, enable/disable, zero-size, error), Cancel batch (confirm, call, hidden for non-owners), the View JSON loading/error/retry states, the Arrange view logic (selection-only, anchor,loop_linkage, unmeasured nodes), and dropdown authoring in the settings popover.
Nits
- Deletion dialog:
- Rows are keyed by
${surface}:${label ?? index}, so two workflows with the same name collide (useDeletionConfirmation.tsx:127). - The forced in-use dialog still offers "Don't ask me again" and its hint, although that preference doesn't apply there.
- Rows are keyed by
- Settings bag:
serializeNodeFieldFormSettingsrebuilds the bag from scratch, so unknown keys are lost on the first edit (formFieldSettings.ts:147). - Cancel batch on a batch with nothing unfinished confirms, then toasts success with "0 queue items were canceled".
- Hardcoded copy:
'Failed to load queue item'(queueDataStore.ts:103) andOption ${n}inWorkflowFieldInput.tsx. - Naming:
- The hotkey settings list
Ias "Toggle Metadata", while the UI says "Details". - The command is still titled "Load workflow from image" although it now handles videos.
- The hotkey settings list
- Invocation cache commands:
clearInvocationCacheandsetInvocationCacheEnableddon't receiveowner.signal. They are harmless (admin-only, idempotent) but inconsistent with the other fenced commands. - Pre-existing, but now contradicts the new comment in
imageRecall.ts: when every recalled reference image has been deleted,fieldsdropsreferenceImages, butsetSettings(result.values)still applies the emptied list, which wipes the current references (executeImageRecall.ts:305).
Verified as sound
- Backend authorization:
- The invocation-cache and
runtime_configroutes are admin-only. - Prune and cancel-by-batch are user-scoped for non-admins.
- Retry checks the root item's owner.
- The invocation-cache and
- UI gating: the UI hides admin controls rather than surfacing 403s.
- Account fencing: the new queue commands capture the account scope and assert it after each request.
- Settings bag compatibility: webv1 bags parse in webv2, and webv2 bags (with
step) parse in webv1's schema. - Clear Finished: the scoped path deletes only terminal roots.
- Linkify: only http(s) URLs become links, with
noopener noreferrer. - Arrange: one undoable step.
- Request counts: unchanged.
Checked against 54902fb; the delta to f5ba648 was re-read by hand:
- Gates: lint, fixtures, performance:architecture, project-files and accessibility pass locally.
- Unit and browser tests: the local failures are pre-existing, locale-dependent, and identical on
main. - Manual pass against the mock backend:
- Queue: retry, cancel, batch cancel, View JSON and Clear Finished Items.
- Invocation cache footer.
- Gallery: video Load Workflow,
Idetails, and the in-use deletion dialog. - Workflows: Arrange + undo.
- Settings: Queue history.
- Not exercised: a real backend and socket events (so finding 1 is traced, not observed), and multi-user mode.
|
This is a really thorough review @Pfannkuchensack, thanks. Working through it all now. |
Pfannkuchensack
left a comment
There was a problem hiding this comment.
Follow-up at 907712a. Everything from the first round is resolved; I re-checked each item against the code. The description and docs now match the shipped behaviour, and the recall wording is right: webv1 also falls back by name only for reference-image adapters. What remains is mostly in the new retry adoption.
Retry adoption
1. A rerun no run adopts is staged in every tab that has the project open.
handleItemsRetried builds its run map from this tab's history only (runtime.ts:1304). Every other tab of the owner, on any device, therefore files the rerun as unadopted. On completion, handleUnadoptedRerunSettled stages it whenever the project is merely open (runtime.ts:1426, :1433). That happens even while the tab that owns the run reopens it and stages the same result at the submitted bbox.
Canvas staging is persisted with the document, so an idle tab that never touched the run writes the project:
- duplicate candidates, one at each tab's current bbox;
- most likely a revision conflict on the second autosave, which
projects/README.mdleaves to an explicit user decision.
Before this delta, only the tab holding the run (under its Web Lock) routed results into the document. The test "stages what another tab's Workflow rerun produced…" asserts the current behaviour, but it doesn't model the owning tab adopting the rerun at the same time.
Suggestion: stage an unadopted Canvas rerun only when no live page holds the run, for example by try-acquiring the existing run-ownership lock for the origin's project and queue item. Otherwise only refresh the Gallery. I traced this from the code; I have not reproduced it with two tabs.
2. Reopening a run moves the Canvas staging selection.
case 'reopenQueueItemForRetry' (workbenchState.ts:5085) doesn't capture getSelectedCanvasStagingSlot or call clampCanvasStagingSelection, unlike setQueueItemStatus, routeQueueItemResults and stageRetriedCanvasResults. Reopen replaces backendItemIds with the rerun id. Staging groups sort by backendItemIds[0], so the run's group moves behind newer runs and gains a placeholder, and the selected index then points at a different candidate.
Example: staging holds [A:c1, B:c1, B:c2] with B:c2 selected. After retrying A, index 2 is A's placeholder.
Suggest wrapping the case like the others, plus a reducer test with two staged runs and a non-first selection.
3. A Canvas retry for a project that isn't open is still delivered nowhere.
With the queue's "all" jobs scope, Retry is offered on Canvas items of other projects (queueOwnership.ts:29). The rerun's output is intermediate, and handleUnadoptedRerunSettled returns when the project isn't open (runtime.ts:1426). The user gets "Retry queued" and the GPU runs, but nothing appears anywhere.
Suggest disabling Retry, with an explanation, for Canvas items whose project isn't open here. Alternatively, say so in the toast or the docs.
4. The HTTP pairing is discarded.
retryItems maps the response to retried_item_ids only (httpRealtimeQueueBackend.ts:89), and RetryItemsResultDTO doesn't declare retried_items (serverTypes.ts:42).
The description lists socket-only adoption as a limitation. Feeding the response's pairs into the same handler in the retrying tab would close most of that gap cheaply. When the event arrives afterwards it does no harm: a second reopen is already refused once the run is running, and adoption checks item ids.
Accessibility
- Queue history: a successful Retry swaps the alert branch for the form and unmounts the focused button, so focus falls to
body(MaxQueueHistorySettings.tsx:100). - View JSON: its Retry has the same pattern. Refetching a never-loaded query replaces the error branch, including its focused button, with the spinner (QueueItemActions.tsx:57).
- Fix: move focus to the input or the dialog body after the load, or keep the button mounted with
loading.
Test gaps
-
Invoke Front, plain dispatch: the plain dispatch in
invocationSubmit.ts(:262) has noprependassertion; only the dynamic-prompt expansion is covered. That path carries every Invoke Front without dynamic-prompt syntax and every Workflow, Upscale and Video submission. -
Journal round-trip:
retriedBackendItemIds(queueRunRestoration.ts:132) has no journal round-trip test. That is the same failure class as the earlierprependloss. -
Mock chain rules: two mock rules are never exercised:
- prune keeps finished children of active roots (mock-backend.mjs:1293);
- cancelling a child cancels the whole chain.
The fixtures contain no
parent_item_id/root_item_id, so reverting either rule stays green. -
Shuffle visibility: it now reads the narrowed template (NodeFieldControl.tsx:121), but no UI test covers it.
-
Cache footer: the status-read error state ("Unavailable") of the invocation cache footer is untested.
Nits
- Ignored step: an authored step that is ignored is still dropped without a warning (formFieldSettings.ts:254). This covers a fractional step on an integer field, or a step that isn't a multiple of the node's
multipleOf.getNodeFieldSettingsIssue(:317) has no case for it. - Stacked JSDoc: at
fields.ts:74the old JSDoc still sits on top of the new one. - Admin sockets: admin sockets receive every user's pairs. Admin tabs therefore likely file other users' reruns as unadopted and invalidate the Gallery on each completion. Filtering to the viewer's own entries would avoid that.
- Query key: the video-capability query key in
ImageContextMenu.tsx:342sits in the gallery namespace but outsidegalleryKeys/getAccountKey. - Test hygiene:
runtime.test.ts:3076asserts on the module-leveladdImagesToGalleryBoardmock, which is never reset. - Description, budgets: it says the budgets pass "against the committed baselines; no re-record needed". The PR does carry a re-record (
73cb580). That re-record addsSliderNumberField.tsxto the Launchpad and editor owner sets andformFieldSettings.tsto the editor set, about +6 KB / +18 KB initial raw. ARCHITECTURE.md asks for this to be stated.
Checks at 907712a
- webv2 gates: lint, fixtures, the architecture/build/browser performance budgets (against the committed baselines), the project-file journeys and accessibility pass locally.
- Unit and browser tests: the only failures are locale-dependent ones that fail identically on
main. - Backend:
test_session_queue_workflow_call_retry.pyand the multi-user socket tests pass, and Ruff is clean. - Manual: against the updated mock, Retry returns the root/rerun pairing and queues the rerun. Adoption itself can't be observed without socket events.
…'s board A second failed item of a video batch retried while the first one's rerun still runs now reaches the run's captured Gallery board instead of staying Uncategorized, as an adopted rerun's video would.
Step multiples are rounded to the decimals a step like 1e-7 actually has, so its slider track and shuffle no longer collapse to 0.
Staging lands in the synced project document, so a tab now stages an unadopted Canvas rerun only while it holds the run's lock; a tab still tracking the run keeps it, and other tabs skip it.
Reopening regroups the run's staging slots under its rerun; the selection now stays on the candidate it showed instead of landing on another run's placeholder.
…s' retries The retrying tab replays its response's pairing to the retry handler, so adoption no longer hangs on the socket event; the handler skips pairs it has already seen. On multiuser servers the runtime keeps only the viewer's own pairs, so admin tabs stop filing every user's reruns.
Retrying a Canvas job whose project is not open in this tab still works, since a tab with it open stages the result; the confirmation now says so, and that nothing shows it when the project is open nowhere.
A successful Retry moves focus to the restored View JSON tab or queue history field, and a retry in flight keeps its button mounted, so focus never falls to the page.
A fractional step on an integer field, or one that is not a multiple of the node's own step, now shows why the form ignores it.
The video context menu's capability read is keyed per account beside the other gallery item queries.
…in rules and cache status errors Adds coverage for prepend on the plain submit path, retriedBackendItemIds surviving the recovery journal, the mock prune and cancel-chain rules (through a test-only seeding route), Shuffle visibility, and the cache footer when its status cannot be read.
…eir run Sibling reruns of one run share this tab's staging claim instead of contending with its own lock. The claim waits for a run lock this tab is still releasing and rechecks its own ownership after each wait.
…t again Adopting or staging a rerun claims it in the account's workbench metadata store, keyed by item and local run. Staging claims only after its results are read and defers to a tab that tracks the run without taking its lock.
…-adopt a delivered one Recheck that the project is open before claiming a rerun's delivery, and give the claim back if it closes meanwhile. Reserve each rerun before reopening its run; a delivered rerun refuses the reservation, and a refused reopen releases it.
Adds POST /queue/{queue_id}/i/{item_id}/delivery to claim, confirm or release
staging a finished item's results, decided by one conditional update per call.
Migration 2026_10_13_add_session_queue_delivery adds three nullable columns.
Reruns are staged only by the tab holding the server's delivery claim; the run's own tab claims at hold and renews, so it keeps the result it retried. Replaces the per-browser IndexedDB claims and reservations; mock backend serves the endpoint.
…anvas (#9805) ## Summary Two Canvas generation paths the legacy frontend supported were missing from webv2. **PiD decoding on Canvas.** The canvas compiler rejected every PiD mode ("not supported on the canvas yet"). It now compiles Canvas text-to-image and image-to-image, in both Fit and Native modes, on every base where Generate already supports PiD. Native image-to-image resizes the source to the quarter-size denoise resolution and decodes straight to the bounding box, as the legacy builders did; Fit keeps the full-size encode and resizes the decoded output. The encode VAE is resolved from the graph (the decode's VAE edge, or the model loader for the FLUX and SD3 decoders that take none). Inpaint/outpaint and Scale Before Processing that changes the processing size remain rejected with the legacy messages. **Anima inpaint adapter.** Anima models get an optional four-channel inpaint/outpaint adapter slot (Anima LLLite models that are not three-channel) with a weight control. During Canvas inpaint and outpaint the graph adds the inverted gradient mask and an `anima_lllite` node fed by the resized/initial image (inpaint) or the infilled image (outpaint), joined to the shared control collector exactly as the legacy `addInpaint`/`addOutpaint` builders wired it. Ordinary control layers still reject four-channel models. The adapter and weight are recorded in metadata and restored by Recall All and Remix. Graphs without PiD or without an adapter are unchanged. ## Related Issues / Discussions Follows up the legacy parity audit behind #9803. ## QA Instructions From `invokeai/frontend/webv2` (Node 22.14, pnpm 10.12): `pnpm check:release` passes (lint, unit, browser, fixtures, performance, project-file journeys, accessibility). One browser suite unrelated to this change (gallery maintenance settings) timed out once under heavy machine load and passed on rerun. Python: `pytest tests/app/invocations/test_frontend_anima_lllite_graphs.py tests/app/invocations/test_frontend_graph_node_types.py` passes (474 passed, 2 skipped). Follow-up at 9b892f1: `pnpm check:release` passes again (lint, unit, browser, performance, project-file journeys, accessibility); `pytest tests/app/invocations/test_frontend_anima_lllite_graphs.py tests/app/invocations/test_frontend_graph_node_types.py`: 480 passed, 2 skipped. The mock backend has no Anima or PiD models, so UI behavior is covered by browser tests (`GenerateSettingsForm.browser.test.tsx`: the Inpaint Adapter slot lists only four-channel models, the weight control appears once one is picked, and the slot is absent for non-Anima models). Real-model verification was not performed. With a real install: 1. Select an SDXL or FLUX model with a PiD decoder and Gemma-2 encoder, set PiD to Fit or Native, draw a bounding box on Canvas and invoke text-to-image and image-to-image: both run. Paint an inpaint mask: the error reads "PiD decode currently supports Text to Image and Image to Image only…". Set Scale Before Processing to Auto on a small box: "Turn off Scale Before Processing…". 2. Select an Anima model, open Generate → Components → Inpaint Adapter: only the inpainting LLLite is listed; pick it and the weight control appears. Add an inpaint mask on Canvas and invoke: the queued graph contains `anima_inpaint_adapter` and `invert_anima_inpaint_adapter_mask`. Recall All on the result restores both. ## Review No material findings. Reviewers confirmed the compiled graphs match the legacy wiring field for field on every PiD base and both adapter paths, and that non-PiD and non-adapter graphs are unchanged. Follow-ups from review: compile tests for scaled outpaint and noise-mask inpaint with the adapter, a recall test for an uninstalled adapter, and the browser test above. Follow-ups from the second review: PiD Fit on an off-grid bbox resized the decoded image twice (to the grid size, then to the bbox); it now resizes once, straight to the bbox. Recall All and Remix did not clear a selected inpaint adapter when the image was made without one; `core_metadata` drops `null` on write, so recall now treats an absent `anima_inpaint_adapter` on an `anima_inpaint`/`anima_outpaint` record as none, and a Python test pins that the invocation persists the key only when an adapter ran. A recorded adapter weight is clamped to the setting's range on recall. Tests added for SDXL Native encode size, the off-grid Fit resize, and an inpaint-with-adapter graph in the backend fixture. Adapters with `cond_in_channels: null` are still offered, as in legacy. Limitations: no real-model execution; a legacy-made Anima inpaint image never recorded its adapter, so recalling one clears the adapter slot (before, it kept whatever was selected; neither restores it); the Scale Before Processing rule compares grid-clamped sizes (webv2's auto scaling only grows), so an already-optimal box is accepted where legacy's auto mode would have rejected it. ## Compatibility / Rollout The new settings are optional with defaults and normalize on project load; no project-document migration. No API changes. ## Checklist - [x] _The PR has a short but descriptive title, suitable for a changelog_ - [x] _Meaningful regression coverage added / updated where needed; obsolete tests/code removed_ - [x] _Persisted-state and API changes include required migrations / compatibility validation_ - [x] _Relevant performance/efficiency opportunities considered; material claims have evidence_ - [x] _Material review findings resolved and relevant checks rerun_ - [x] _Documentation added / updated (if applicable)_ - [ ] _Updated `What's New` copy (if doing a release after this PR)_ <!-- codesmith:footer --> --- <a href="https://app.blacksmith.sh/invoke-ai/codesmith/InvokeAI/pr/9805?autoLogin=true&ref=codesmith_pr_footer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v3.svg"><img alt="View with [code]smith" src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3.svg"></picture></a> <a href="https://backend.blacksmith.sh/track/enable-autofix?expires=1794282812&installation_model_id=441795&pr_number=9805&ref=codesmith_pr_footer&repository=invoke-ai%2FInvokeAI&return_to=https%3A%2F%2Fgithub.com%2Finvoke-ai%2FInvokeAI%2Fpull%2F9805&signature=eeb9318c56c3b885f03d3c4ca44ee75c8e836ecb11be415370e017140b698883"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img alt="Autofix with [code]smith" src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a> <sup>Need help on this PR? Tag <code>@codesmith-bot</code> with what you need. Autofix is disabled.</sup> <!-- codesmith:autofix:disabled --> <!-- /codesmith:footer -->
…nd output filters (#9809) ## Summary Model-family tuning controls and two output options from the legacy frontend were missing from webv2 Generate and Canvas. - **FLUX.1 DyPE**: preset (off, auto, area, 4K optimized, manual) with scale and exponent under Manual, sent to the FLUX denoise node only when set. - **Z-Image**: an optional timestep shift override and the seed-variance enhancer (strength and randomized channels), inserted between the positive conditioning and its collector exactly as legacy wired it. - **Qwen-Image**: timestep shift override and encoder quantization (none, int8, nf4) applied to both text encoders. - **Krea-2 style reference**: a new reference-image kind with a 0–2 style strength, compiled to `krea2_style_reference` fed by the (cropped) image and the model VAE and connected to denoising; strength 0 bypasses it. Krea-2 now advertises one reference image on the backend. - **NSFW checker and invisible watermark**: two project settings (Settings → Project, off by default) that chain `img_nsfw` / `img_watermark` after the final image of every built-in Generate and Canvas family, keeping the output flags and metadata on the last node. Every control appears only for its model family; graphs at defaults are structurally unchanged. New settings are emitted in metadata and restored by Recall All and Remix. ## Related Issues / Discussions Follows up the legacy parity audit behind #9803. ## QA Instructions From `invokeai/frontend/webv2` (Node 22.14, pnpm 10.12): `pnpm check:release` passes. Python: `pytest tests/backend/architectures tests/app/invocations/test_frontend_graph_node_types.py` passes. The mock backend's representative profile has only SD 1.x main models, so the per-family controls are covered by browser tests (`GenerateRenderSection`, `GenerateAdvancedFields`) and compile tests. On the mock: open a project → Settings → Project: toggle NSFW checker and Invisible watermark; the Generate graph preview then ends `… → img_nsfw → img_watermark`. With a real install: select a FLUX.1 model → Render → DyPE = Manual shows λs/λt; Qwen-Image → Advanced → Encoder quantization; Z-Image → Shift override and Seed variance; Krea-2 → add a reference image → Style strength. Generate, then Recall All restores each. ## Review No material findings. Reviewers confirmed every field against the backend invocations and the legacy builders, including that DyPE scale/exponent are read only under the manual preset. Follow-ups applied from review: tests for the shift override being turned off, watermark-only Canvas outpaint to a staging destination, the project setting flowing through the normal invoke path, and recall of a style reference whose image is missing; a single "Shift" label; docs naming. Limitation: `core_metadata` at defaults now records `z_image_shift: 'auto'`, `qwen_image_shift: 'auto'`, `qwen_image_quantization: 'none'` and the seed-variance keys, matching legacy; execution nodes are unchanged. ## Compatibility / Rollout New generate settings and the two project settings are optional with defaults and normalize on load; no project-document migration. The Krea-2 architecture definition now reports `max_reference_images=1` (capabilities fixture regenerated). No API changes. ## Checklist - [x] _The PR has a short but descriptive title, suitable for a changelog_ - [x] _Meaningful regression coverage added / updated where needed; obsolete tests/code removed_ - [x] _Persisted-state and API changes include required migrations / compatibility validation_ - [x] _Relevant performance/efficiency opportunities considered; material claims have evidence_ - [x] _Material review findings resolved and relevant checks rerun_ - [x] _Documentation added / updated (if applicable)_ - [ ] _Updated `What's New` copy (if doing a release after this PR)_ <!-- codesmith:footer --> --- <a href="https://app.blacksmith.sh/invoke-ai/codesmith/InvokeAI/pr/9809?autoLogin=true&ref=codesmith_pr_footer"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v3.svg"><img alt="View with [code]smith" src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3.svg"></picture></a> <a href="https://backend.blacksmith.sh/track/enable-autofix?expires=1794299596&installation_model_id=441795&pr_number=9809&ref=codesmith_pr_footer&repository=invoke-ai%2FInvokeAI&return_to=https%3A%2F%2Fgithub.com%2Finvoke-ai%2FInvokeAI%2Fpull%2F9809&signature=a5b0cac6b23ab2f17cdddedc238595565b5f9a62e33326c955ddbbc3025b3311"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img alt="Autofix with [code]smith" src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a> <sup>Need help on this PR? Tag <code>@codesmith-bot</code> with what you need. Autofix is disabled.</sup> <!-- codesmith:autofix:disabled --> <!-- /codesmith:footer -->
The scroll helper read the grid viewport right after render, so on a slow runner the listing was not mounted yet and the test failed on a null viewport.
Summary
A parity audit of the legacy frontend found behaviors that webv2 lost or only half implemented. This PR restores the ones that are correctness fixes or self-contained UI work; model-family generation features, storage maintenance, the external model editor and board/workflow sharing are left for their own PRs.
Recall: Recall All / Remix now restores legacy FLUX
guidance, Krea-2 seed variance, and an explicitly empty saved reference list (which clears the current references instead of leaving them). As in the legacy frontend, the main model, components, VAE and LoRAs are matched strictly by key or recorded hash; only reference-image adapter models fall back to name, base and type when their hash is gone, and an ambiguous name match is refused. When every recorded reference image has since been deleted, the current references are kept.Gallery: deleting media the current project still uses always asks first, even with the confirmation preference off, and the dialog lists the affected surfaces. Videos gain Load Workflow in the context menu and through
W.Itoggles Preview details.Queue:
Ctrl/Cmd+Shift+Enter) sendsprepend: truefor every submission source, including a run replayed from the recovery journal after a reload.Workflows: authored form settings (string input/text area/dropdown with options; number input/slider/combined with min/max/step) are parsed from the legacy settings bag, rendered, honoured by validation, shuffle and the Invoke gate, round-trip unchanged (unknown keys included), and are editable in the form builder. Bounds that are inverted or lie beyond the node's own limits are ignored with a warning, and a step that leaves no valid value is flagged on every control type. URLs in form text and field help are links. An Arrange nodes action lays out the whole graph or the selection, undoably, with unconnected nodes packed into a grid.
Related Issues / Discussions
Addresses the deletion warning part of #9618. Sharing (#9673) is intentionally not part of this PR.
QA Instructions
Run from
invokeai/frontend/webv2(Node 22.14, pnpm 10.12):check:releasepasses on the final head: lint/typecheck/architecture, 10,238 unit, 2,937 browser and 45 fixture tests, the architecture, build and browser performance budgets, the project-file journeys and the accessibility checks. Each commit also typechecks on its own.Performance: the byte baselines were re-recorded once in this PR (
73cb580).SliderNumberField.tsxjoins the Launchpad and editor initial owner sets andformFieldSettings.tsthe editor's: Launchpad initial +6,025 B raw / +1,791 B gzip, editor +18,165 B raw / +5,612 B gzip. Request counts are unchanged, and later commits pass against that baseline without another re-record. Python:pytest tests/app/services/session_queue tests/app/routers/test_multiuser_authorization.py tests/app/routers/test_session_queue_delivery_route.py tests/app/services/shared/sqlite_migrator tests/app/services/shared/database(730 passed, 21 skipped) and Ruff are clean on SQLite; the MySQL/MariaDB variants were not run. The docs build passed with all internal links valid before a final prose-only edit to the retry paragraph.Manual pass against the mock backend (
scripts/mock-backend.mjs, representative profile):I: the details popover toggles.max_size: 0its controls are disabled with an explanation.Ctrl+Zrestores the previous positions.Not exercised against a live backend: retry adoption and staging across tabs and browsers (the mock emits no socket events and never runs items; covered by runtime tests, including one wired to the real workbench reducer, and by service and route tests of the delivery claim), workflow-form number warnings in the browser (the mock has no number-field node; covered by browser tests), real GPU generation, a multi-user server, and prepend ordering.
Review
Material findings resolved:
Remaining limitations:
hasVideoWorkflowfalls back to a per-open video record fetch when the mixed listing lackshas_workflow; other image context-menu labels remain hardcoded English.Compatibility / Rollout
RetryItemsResultand thequeue_items_retriedevent gainretried_items: [{source_item_id, item_id}], defaulting to[];retried_item_idsis unchanged. Socket delivery scopes the pairs to each owner's room (admins see all, the sanitized companion none).invokeai/frontend/apiartifacts are regenerated. With an older server the pairing is absent, so reruns are not adopted and their images reach the Gallery as they did before this PR.POST /api/v1/queue/{queue_id}/i/{item_id}/delivery(claim_queue_item_delivery) claims, confirms or releases staging an item's results; only the item's owner or an admin may call it.2026_10_13_add_session_queue_deliveryadds three nullablesession_queuecolumns (delivery_claimant,delivery_claimed_at,delivered_at); queue item responses don't include them.prependon the snapshot,retriedBackendItemIdson the run); records without them restore as before, and malformed values are dropped.settingsbag shape (with an optionalstepextension), so forms authored in either editor open in the other.Checklist
What's Newcopy (if doing a release after this PR)Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.