Files
ComfyUI_frontend/browser_tests/assets/missing
jaeone94 caabebe145 Redesign missing model detection contract for promoted subgraph widgets (#13059)
## Summary

Redesign missing-model detection for ADR 0009 promoted subgraph widgets
so candidates are created from the widget value the user can actually
edit, while still using the concrete interior widget as the
schema/options source.

## Why This PR Exists

This PR comes from the follow-up missing-model detection work for the
ADR 0009 / 1.46 subgraph widget changes introduced by
[#12197](https://github.com/Comfy-Org/ComfyUI_frontend/pull/12197).

[#12197](https://github.com/Comfy-Org/ComfyUI_frontend/pull/12197)
intentionally changed promoted subgraph widgets to be represented
through subgraph input links. After that change, the promoted widget on
the host `SubgraphNode` is the editable value owner, and linked interior
widgets are no longer guaranteed to mirror that value.

The old missing-model contract still treated the concrete interior node
widget as the effective source of truth in subgraph cases:

- recursive scans entered the subgraph and scanned the interior widget
value;
- candidates were keyed by the interior node/widget identity;
- the parent subgraph host mostly received propagated
highlight/navigation behavior;
- subgraph container widgets were not treated as first-class candidate
sources.

That contract breaks after ADR 0009. A user can resolve a missing model
by changing the promoted host widget to an installed model, while the
linked interior widget can still hold the old stale value. If detection
keeps scanning the linked interior value, entering the subgraph or
reloading the workflow can re-create a false missing-model error that no
longer corresponds to the value the user can edit.

ADR 0009 also means the same subgraph definition can be reused by
multiple `SubgraphNode` hosts. Once missing-model detection moves from
the interior definition widget to the promoted host widget, the selected
value is no longer a property of the shared definition alone. It is a
property of a specific host instance. That makes the old interior-node
identity insufficient for mode changes, removal handling, and re-scan
behavior: a single interior leaf definition can be reachable through
multiple host execution paths, and only the affected host path should
add, remove, or restore a candidate.

This PR also builds on
[#12990](https://github.com/Comfy-Org/ComfyUI_frontend/pull/12990),
which narrowed workflow-level `models[]` and embedded model data to
metadata enrichment only. Together, the intended boundary is:

- live widgets create missing-model candidates;
- workflow/root-level `models[]` and node metadata only enrich
candidates that already came from a live widget;
- promoted widget values are read from the editable host widget, not
inferred from stale interior `widgets_values`.

## Changes

- **What**:
  - Introduces promoted-widget scan targets that split:
    - host promoted widget value and candidate identity;
    - concrete leaf widget/node definition data.
- Scans the outermost unlinked promoted widget on a `SubgraphNode` host
as the selected value owner.
- Skips linked interior widgets as candidate sources, preventing stale
linked widget values from producing duplicate or false missing-model
candidates.
- Resolves the concrete leaf widget for combo options, asset-widget
support, node type, directory lookup, and embedded metadata enrichment.
- Keys promoted missing-model candidates by the host execution id and
host promoted widget name.
- Adds `sourceExecutionId` to promoted candidates so liveness still
follows the concrete source execution path, including nested inactive
subgraph containers.
- Uses source-scope activity for pipeline filtering and async
verification, while keeping highlight/store/clearing identity
host-keyed.
- Removes host-keyed promoted candidates when their source execution
scope is removed or bypassed.
- Re-scans ancestor subgraph hosts when an interior source path is
un-bypassed, so host-keyed promoted errors can reappear correctly.
- Handles shared subgraph definitions by deriving promoted source paths
from the concrete host instance path, rather than treating the shared
definition node id as globally unique.
- Shares promoted source resolution between Vue node processing and the
right-side panel to avoid drift.
- Aligns missing-model clearing across Vue node widgets, legacy canvas
widgets, and right-side panel Parameters/Nodes section widgets.
- Adds unit coverage for scan identity, source-scope liveness, dynamic
mode changes, source-scope removal, shared-definition host isolation,
and right-side panel clearing.
- Adds nested promoted-widget E2E coverage for OSS and Cloud flows
across Vue, Parameters tab, and legacy widget surfaces.
- **Breaking**: None expected.
- **Dependencies**: None.

## New Detection Contract

A missing-model candidate is created from an unlinked final editable
value owner.

That value owner can be:

- a normal node model widget; or
- the outermost promoted model widget displayed on a `SubgraphNode`
host.

For promoted widgets:

- the host promoted widget supplies the selected value;
- the host execution id and host widget name are the candidate identity;
- the concrete leaf widget supplies definition data such as combo
options and asset-browser support;
- the concrete source execution path is retained as `sourceExecutionId`
for liveness only;
- linked interior widgets are skipped as candidate sources because their
values are not authoritative when driven by a promoted input.

For a nested chain:

`Outer promoted widget A -> inner promoted widget B -> concrete widget
C`

only `A` creates the candidate. `B` and `C` are linked along the
promoted-input path and are skipped as selected-value sources, while `C`
still provides the concrete widget definition used to evaluate `A`.

## Shared Definition And Source-Scope Liveness

ADR 0009 promoted widgets make subgraphs behave more like reusable
definitions with host-owned inputs. Two host `SubgraphNode`s can point
at the same interior subgraph definition while carrying different
promoted widget values. In that shape, the missing-model candidate must
be keyed to the editable host surface, but the activity check cannot use
the host id alone.

For example, if two outer hosts share the same nested subgraph
definition, one host can select a valid model while the other still
selects a missing model. The result should be one missing-model
reference, not a single definition-level error and not two errors after
one host is fixed. Likewise, bypassing or un-bypassing an interior
nested container should affect only the host execution paths that
actually pass through that container.

This PR therefore separates two concepts:

- **candidate identity**: host execution id + host promoted widget name,
used for storage, highlight, navigation, and clearing;
- **candidate liveness**: concrete source execution path, used for
scan-time activity checks, pipeline filtering, async verification,
source-scope removal, and re-exposure after mode changes.

That separation is the reason this PR updates more than the scan itself.
Moving the detection target to the subgraph host also requires the
mode-change and removal paths to understand that a host-keyed candidate
can be invalidated by a descendant source path, and can need to be
restored by re-scanning an ancestor host when an interior source path
becomes active again.

## Review Focus

Please review the identity split carefully:

- candidate/store/highlight/clearing identity should remain host-keyed
for promoted widgets;
- liveness should use `sourceExecutionId` when present, falling back to
`nodeId` for normal candidates;
- scan-time activity checks should account for the source node itself
and all ancestor subgraph containers;
- source-scope removal should remove host-keyed candidates whose
concrete source path was removed or bypassed;
- un-bypassing an interior source path should re-scan affected ancestor
subgraph hosts so host-keyed candidates can reappear;
- shared subgraph definitions should not merge errors across different
host instances;
- linked interior widgets should not produce their own missing-model
candidates;
- asset-browser eligibility should be resolved from the concrete leaf
node type and widget name, not the synthetic subgraph host type;
- right-side panel edits should clear host missing-model errors and
source validation errors consistently.

The E2E matrix intentionally keeps nested promoted workflows only.
Nested promoted widgets cover the same editable host path as single
promoted widgets while also exercising the `A -> B -> C` chain that can
break source-scope liveness and re-scan behavior. The nested fixture
also includes multiple host instances that share the same subgraph
definition, so it verifies that fixing one host does not accidentally
clear or suppress another host's missing-model candidate. Direct/single
promoted behavior is still covered at the unit level.

## Non-Goals

- This PR does not reintroduce workflow-level `models[]` candidate
creation.
- This PR does not infer selected model values from `widgets_values`.
- This PR does not synchronize linked interior widget values back from
promoted host widgets.
- This PR does not redesign missing-media scanning; missing media still
skips subgraph containers and remains keyed by concrete interior paths.
The shared async post-verification active-scope filter is intentionally
stricter, so a pending missing-media candidate is no longer surfaced if
its own node is bypassed or removed while verification is in flight.

## Validation

- `pnpm exec vitest run
src/components/rightSidePanel/parameters/SectionWidgets.test.ts
src/platform/missingModel/missingModelScan.test.ts
src/composables/graph/useErrorClearingHooks.test.ts
src/platform/missingModel/missingModelPipeline.test.ts
src/platform/missingModel/missingModelStore.test.ts
src/utils/graphTraversalUtil.test.ts
src/composables/graph/useGraphNodeManager.test.ts
src/renderer/extensions/vueNodes/composables/useProcessedWidgets.test.ts
--reporter=dot`
  - 8 files passed, 294 tests passed.
- `pnpm exec vitest run
src/platform/missingModel/missingModelScan.test.ts
src/core/graph/subgraph/resolveConcretePromotedWidget.test.ts
src/components/rightSidePanel/parameters/SectionWidgets.test.ts`
  - 3 files passed, 71 tests passed.
- `pnpm typecheck`
- `pnpm typecheck:browser`
- `pnpm format:check`
- targeted ESLint for changed production/unit/E2E files
- `git diff --check`
- `pnpm build`
- `pnpm build:cloud`
- OSS affected E2E on the 8188 build:
- `PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://localhost:8188 pnpm
exec playwright test
browser_tests/tests/propertiesPanel/errorsTabModeAware.spec.ts
--project=chromium --grep "Changing an OSS .*promoted|Refreshing a
resolved promoted|Reloading a resolved nested"`
  - 5 passed.
- Cloud affected E2E on the 8188 cloud build:
- `PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://localhost:8188 pnpm
exec playwright test
browser_tests/tests/propertiesPanel/errorsTabCloudMissingModels.spec.ts
--project=cloud --grep "Changing a Cloud .*promoted"`
- 2 passed; the Cloud legacy promoted asset-modal case still fails until
[#13075](https://github.com/Comfy-Org/ComfyUI_frontend/pull/13075) is
merged.
- Full OSS `errorsTabModeAware.spec.ts` on the 8188 build:
- 23 passed; 3 existing paste/clipboard cases failed before the promoted
subgraph section with node count remaining at 1 after
`clipboard.paste()`.
- Commit hooks ran `oxfmt`, `oxlint`, `eslint`, `pnpm typecheck`, and
browser typecheck where applicable.
- Pre-push hook ran `pnpm knip --cache`.

## Screenshots

Before


https://github.com/user-attachments/assets/6380c1da-1d92-4b70-888e-3ade572c4b5b

After


https://github.com/user-attachments/assets/4cfc24d6-3dc3-4e36-9b31-72fea6b3d9d5
2026-06-26 19:38:12 +00:00
..