Skip to content

feat: restore legacy recall, deletion, queue and workflow-form behaviors - #9803

Merged
joshistoast merged 45 commits into
mainfrom
feat/webv1-parity-batch
Oct 17, 2026
Merged

joshistoast merged 45 commits into
mainfrom
feat/webv1-parity-batch

Conversation

@joshistoast

@joshistoast joshistoast commented Oct 11, 2026 •

Copy link
Copy Markdown
Collaborator

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. I toggles Preview details.

Queue:

  • Invoke Front (Ctrl/Cmd+Shift+Enter) sends prepend: true for every submission source, including a run replayed from the recovery journal after a reload.
  • Failed and canceled items can be retried, and the results go where the original run was headed: the run that owns the item reopens around the rerun, so Canvas results stage at the submitted bbox and Gallery results land on the run's board. This also holds when several failed items of one batch are retried one after another. A rerun no open run can adopt (after a reload, or once that Canvas was replaced) still reaches the Gallery, or is staged at the current bbox by a tab with the project open. A Canvas rerun is staged once across tabs, browsers and devices: the server records which tab claimed its delivery, an unconfirmed claim lapses after two minutes, and the run you retried claims its reruns while its tab holds the run, so it keeps the result even when another browser sees it finish first. A tab claims a rerun only while the project is open there and releases the claim if it closes meanwhile. Retrying a Canvas job whose project is closed in this tab says the result goes to a tab where it is open. The retrying tab adopts from its own response as well as from the socket event, and admins' tabs ignore other users' retries. Utility jobs (filters, layer workflows) are not offered Retry.
  • Waiting workflow-call parents are cancellable and labelled as waiting. Expanded rows offer Cancel batch, which says so when nothing was left to cancel; the header menu offers Clear Finished Items. View JSON fetches the saved graph, results, workflow and traceback.
  • Admins get an invocation cache footer (status, clear, enable/disable; disabled with an explanation when the server has no cache capacity) beside the model cache, and a Queue history setting under Settings → Server that shows Retry instead of an editable field when the server's value can't be read.
  • Queue refreshes no longer refetch open details of finished items, and scoped Clear Finished / Clear Failed choose roots from item summaries instead of hydrating every session.

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):

pnpm check:release

check:release passes 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.tsx joins the Launchpad and editor initial owner sets and formFieldSettings.ts the 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):

  1. Gallery → add an image as a Generate reference image → delete it: the dialog lists "Generate reference images" and offers no "Don't ask me again".
  2. Preview focused, press I: the details popover toggles.
  3. Right-click a video or image: Load Workflow is present (disabled when the media carries no workflow).
  4. Automate layout → Queue: failed/canceled rows show Retry and retrying queues a rerun; waiting rows show Cancel; an expanded row shows Cancel batch and View JSON with Item/Graph/Results tabs; the header menu shows Clear Finished Items; the Invocation cache footer reads the status, and with max_size: 0 its controls are disabled with an explanation.
  5. Workflow editor → Arrange nodes on the 100-node fixture: nodes form a 10×10 grid; Ctrl+Z restores the previous positions.
  6. Workflow widget → Edit → pin a string field → field settings → Dropdown with two options → View tab: the field is a select and picking an option writes the node value.
  7. Settings → Server → Queue history: enter a value and Save; the restart notice appears. Make the runtime config read fail: Retry replaces the field and restores it once the read succeeds.

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:

  • Retried runs lost their original destination; they now return to the run's captured Canvas bbox or Gallery board. Retrying a second failed item of a partly retried batch, results its other items had delivered, a pending recovery-journal settle racing a reopened run, Upscale/Workflow → Canvas reruns no open run adopts, video reruns an active run cannot adopt, every open tab staging the same unadopted rerun (at once or one after another, including after the first tab closed or after an adopting tab let its run go), two browsers or devices staging the same rerun, other tabs waiting for a reload after the run's own tab closed, a result left unstaged when a tab closed between adopting and recording a rerun or between claiming and staging it, a run refusing to reopen just after its rerun completed elsewhere, a tab whose project closed keeping the claim, a run that learns of a retry late re-adopting a rerun another tab already staged, sibling reruns of one run finishing while that tab is still staging the first, a reopened run moving the staging selection, and adoption depending on the socket alone are all handled and covered. Retry now moves focus to the restored view instead of dropping it.
  • Model recall claimed a name fallback the legacy frontend never had for main/VAE/LoRA models; recall is strict-hash except for reference-image adapters, matching legacy.
  • Invoke Front was lost when a run was replayed from the recovery journal.
  • The invocation cache footer offered commands the server ignores at zero capacity; the queue history editor could show an unreadable value as an editable "keep all" and overwrite it.
  • Authored form bounds beyond the node's limits produced unsatisfiable fields; warnings appeared only on sliders; a step written in exponent form (1e-7) collapsed the slider and shuffle to 0; an ignored step gave no warning.
  • A scoped Clear Finished reported failure on workflow-call chains (only terminal roots are deleted now). Retry, Cancel batch and the cache commands are account-fenced like the other queue commands.

Remaining limitations:

  • A tab that closes after staging a Canvas rerun but before the server records the delivery (or loses that confirmation to a network error) lets another tab stage it again two minutes later; the second copy shows only if the first tab's project had saved. A tab suspended for over two minutes loses its claim, so another tab stages the result at its own current bbox, and if that tab then fails to stage it, no tab does.
  • Against a server without the delivery endpoint, each tab with the project open stages an unadopted Canvas rerun.
  • A rerun no run adopted is remembered in memory only, so one whose completion event is missed while the socket reconnects is not delivered; a Canvas rerun whose project is open nowhere is never shown.
  • A Workflow → Canvas rerun no open run adopts is staged from images created after it was queued, so a rerun served entirely from the invocation cache stages nothing.
  • The canvas node card does not mirror an authored-bound violation (the form field and the Invoke gate do); hasVideoWorkflow falls back to a per-open video record fetch when the mixed listing lacks has_workflow; other image context-menu labels remain hardcoded English.

Compatibility / Rollout

  • API (additive): RetryItemsResult and the queue_items_retried event gain retried_items: [{source_item_id, item_id}], defaulting to []; retried_item_ids is unchanged. Socket delivery scopes the pairs to each owner's room (admins see all, the sanitized companion none). invokeai/frontend/api artifacts 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.
  • API (additive): 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.
  • Database: migration 2026_10_13_add_session_queue_delivery adds three nullable session_queue columns (delivery_claimant, delivery_claimed_at, delivered_at); queue item responses don't include them.
  • Persisted data: queue runs are never part of project documents, so project schema 3 is unchanged. The per-browser recovery journal gains two optional fields (prepend on the snapshot, retriedBackendItemIds on the run); records without them restore as before, and malformed values are dropped.
  • Workflow JSON keeps the legacy settings bag shape (with an optional step extension), so forms authored in either editor open in the other.

Checklist

  • The PR has a short but descriptive title, suitable for a changelog
  • Meaningful regression coverage added / updated where needed; obsolete tests/code removed
  • Persisted-state and API changes include required migrations / compatibility validation
  • Relevant performance/efficiency opportunities considered; material claims have evidence
  • Material review findings resolved and relevant checks rerun
  • Documentation added / updated (if applicable)
  • Updated What's New copy (if doing a release after this PR)

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

…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.
@github-actions github-actions Bot added frontend PRs that change frontend files docs PRs that change docs labels Oct 11, 2026
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 Pfannkuchensack left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_uploaded or 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_config read fails, the store holds maxQueueHistory: 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.
  • ?? value after the PATCH shows a server null as the requested value (generationDevicesStore.ts:165).
  • If the unrelated generation_device_options request 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_id does 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:153 still 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 in round_robin. In multi-user FIFO, prepend priority is the queue-wide top pending priority + 1, and _FIFO_NEXT orders 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 (via ROUTE_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.itemDetail sits 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_ids to read only status and parent_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 dispatches submitResolvedInvocationSnapshot and submitCanvasInvocationSnapshot. These forwarding sites are untested:

    • workbenchState.ts:4847 and :5407
    • invocationSubmit.ts
    • prepareCanvasInvocation.ts
    • the workflow branch in runtime.ts

    Dropping prepend at any of them would stay green. invocationSubmit.test.ts and prepareCanvasInvocation.test.ts already have harnesses that can assert it.

  • Max queue history: blank → null (keep all) vs 0 (prune all) is untested. Number('') === 0 makes 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.
  • Settings bag: serializeNodeFieldFormSettings rebuilds 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) and Option ${n} in WorkflowFieldInput.tsx.
  • Naming:
    • The hotkey settings list I as "Toggle Metadata", while the UI says "Details".
    • The command is still titled "Load workflow from image" although it now handles videos.
  • Invocation cache commands: clearInvocationCache and setInvocationCacheEnabled don't receive owner.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, fields drops referenceImages, but setSettings(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_config routes are admin-only.
    • Prune and cancel-by-batch are user-scoped for non-admins.
    • Retry checks the root item's owner.
  • 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, I details, 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.

Copy link
Copy Markdown
Collaborator Author

This is a really thorough review @Pfannkuchensack, thanks. Working through it all now.

@Pfannkuchensack Pfannkuchensack left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.md leaves 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 no prepend assertion; 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 earlier prepend loss.

  • 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:74 the 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:342 sits in the gallery namespace but outside galleryKeys/getAccountKey.
  • Test hygiene: runtime.test.ts:3076 asserts on the module-level addImagesToGalleryBoard mock, 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 adds SliderNumberField.tsx to the Launchpad and editor owner sets and formFieldSettings.ts to 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.py and 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.

joshistoast and others added 16 commits October 12, 2026 16:54
…'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.
lstein added a commit that referenced this pull request Oct 17, 2026
…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 -->
@joshistoast
joshistoast enabled auto-merge (squash) October 17, 2026 09:24
joshistoast added a commit that referenced this pull request Oct 17, 2026
…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.
@joshistoast
joshistoast merged commit f76fd04 into main Oct 17, 2026
17 checks passed
@joshistoast
joshistoast deleted the feat/webv1-parity-batch branch October 17, 2026 18:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api docs PRs that change docs frontend PRs that change frontend files python PRs that change python files python-tests PRs that change python tests services PRs that change app services

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants