[Issue 101][eval-and-fix] Audit forward-path device and dtype casts (closes pollockjj/mydevelopment#185)#49
Conversation
…closes pollockjj/mydevelopment#185) Kosinkadink's PR Comfy-Org#11294 review (comment 4259303182) flagged five forward-path device-override sites in comfy/ldm/seedvr/model.py. Yousef removed four; MMModule.forward retained the residual: device = comfy.model_management.get_torch_device() vid = vid.to(device) This pattern fights Comfy's model-management system: load_device already places parameters on a chosen device, and inputs should arrive on the same device. The global get_torch_device() call overrides whatever placement Comfy decided. The cast is removed; comfy.model_management is no longer referenced in this file (import dropped). The surviving line below the removed cast (txt = txt.to(device=vid.device, dtype=vid.dtype)) follows vid.device — Comfy-native pattern, identical to the one NaMMRotaryEmbedding3d.forward already uses. Regression coverage in tests-unit/comfy_test/test_seedvr_forward_no_device_cast.py pins both layers of the fix: 1. Source-level (AST-walked): no forward method body in comfy/ldm/seedvr/model.py calls get_torch_device(); the file no longer references comfy.model_management at all. 2. Smoke forward: MMModule(nn.Linear, ...) runs on a CUDA device and outputs stay on the input device — under shared_weights, vid_only, and standard branches. 74 pre-existing SeedVR2 tests remain bit-identical pass.
…lign tests to contract
Restore the top-level ``import comfy.model_management`` so the diff
between origin/issue_101 and issue_185 contains exactly the two cast
lines inside ``MMModule.forward`` (lines 624-625 on origin/issue_101)
and nothing else. Rewrite ``test_seedvr_forward_no_device_cast.py`` to
two pytest functions matching the slice contract:
- test_no_get_torch_device_in_forward_methods: AST-walks every
``forward`` method body in comfy/ldm/seedvr/model.py and asserts
none calls ``get_torch_device``. Reports ``(func_lineno,
call_lineno)`` so the pre-fix failure produces ``[(613, 624)]``.
- test_mmmodule_forward_succeeds_without_get_torch_device_lookup:
monkeypatches ``comfy.model_management.get_torch_device`` to raise,
then runs ``MMModule(nn.Linear, 16, 8, shared_weights=False)``
forward. With the cast removed the call must complete; with the
cast present it raises the planted RuntimeError.
Closes pollockjj/mydevelopment#185
… slice 1 contract Rewrite the second test to match the contract shape: zero-arg boom that increments call_count[0] before raising, local _IdentityCallable nn.Module returning x, MMModule(_IdentityCallable, shared_weights=False, vid_only=False), mm.forward(torch.zeros(2, 4), torch.ones(2, 4)), and assert call_count[0] == 0 plus identity/device equality on both outputs. Rewrite the first test to use the contract list-comprehension shape against ast.parse(inspect.getsource(...)) on the seedvr model module.
Codex Review — Cycle 1Overall: patch is correct No findings. |
There was a problem hiding this comment.
Pull request overview
Removes the last SeedVR2 MMModule.forward device lookup/cast that bypassed ComfyUI's model-management placement, and adds a regression test module to lock that invariant in place.
Changes:
- Deletes the
comfy.model_management.get_torch_device()lookup andvid.to(device)cast fromMMModule.forward. - Adds an AST-based regression test asserting no
forwardmethod incomfy.ldm.seedvr.modelcallsget_torch_device. - Adds a smoke test proving
MMModule.forwardcompletes without touchingget_torch_device.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
comfy/ldm/seedvr/model.py |
Removes the residual forward-path global device cast from MMModule.forward. |
tests-unit/comfy_test/test_seedvr_forward_no_device_cast.py |
Adds regression coverage for the no-lookup invariant and a direct MMModule.forward smoke test. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Melian PR-Review — Cycle 1 — DECISION: STOP — PR-review cycle converged.HEAD SHA reviewed: Why we stoppedPer the PR-review gate rule (2026-05-05), the cycle stops iff both hold:
Both conditions are met. Any remaining advisory findings below are on test/doc paths or have been ACCEPT-classified by the slicer — they are converged work, not blockers. Raw signal
Verdict rationale (raw)
This comment is posted by Melian's FSM ( |
Plan: Remove residual
comfy.model_management.get_torch_device()cast fromMMModule.forwardOverview
comfy/ldm/seedvr/model.pyonpollockjj/ComfyUI:origin/issue_101HEAD98df2309carries one residual forward-path device lookup that fights Comfy's model-management system:MMModule.forward(file lines 613–631) readsdevice = comfy.model_management.get_torch_device()at line 624 and forces the input viavid = vid.to(device)at line 625 — the exact pattern Kosinkadink flagged on Comfy-Org#11294 (issue-comment4259303182, item 4) and the only one of Kosinkadink's five flagged sites that survived Yousef'sFixed 3, 4reply. Phase 0 ground-truth probes (GTP-2, GTP-3) confirm oneget_torch_device()call in anyforwardmethod body across the file (MMModule.forward, line 624). The slice removes those two lines, locks the repository invariant via an AST-walked regression that asserts zeroget_torch_device()calls inside anyforwardmethod body in this file, and adds a behavioral smoke test that monkey-patchescomfy.model_management.get_torch_deviceto raise — proving the post-fix forward never looks it up — while exercisingMMModule.forward(vid, txt)end-to-end against annn.Identity-shaped stub on CPU. Companion GTP-3 reports one.to(device=..., dtype=...)keyword cast at line 629; that cast is a derived-device cast (device=vid.device) sourced from the input stream, not from a Comfy lookup, and is left untouched per the issue body's specific scope to "lines 624-625" (Out of Scope §). The fix lands onpollockjj/ComfyUI:issue_185cut fromorigin/issue_101HEAD98df2309and PRs back intoissue_101, mirroring the merged sequence #46 / #44 / #43 / #36 / #35 / #34 / #33 / #32 / #31.Diagnosis Summary
The buggy method body, captured live from
origin/issue_101:comfy/ldm/seedvr/model.pylines 613–631 (Ground Truth Probe GTP-4):Lines 624 (
device = comfy.model_management.get_torch_device()) and 625 (vid = vid.to(device)) are the residual cast. Comfy's model-management system places weights and inputs onload_devicebeforeforwardis invoked; an inner module re-querying the globalget_torch_device()and forcing the input there bypasses Comfy's per-modelload_devicedecision (which can differ from the global probe under partial offload, low-VRAM, or CPU-fallback configurations). Removing both lines letsvidride whatever device Comfy passed in, which is the Comfy-conventional contract.The method-local
txt = txt.to(device=vid.device, dtype=vid.dtype)at line 629 is structurally distinct: its device target isvid.device(derived from the input stream, post-vid_modulecall), notcomfy.model_management.get_torch_device(). Phase 0 reports it for completeness but the issue body explicitly scopes the deliverable to "the remaining cast atMMModule.forward(lines 624-625)" — Kosinkadink'sget_torch_device()+.to(device)pattern. Line 629 also carries adtype=vid.dtypecomponent whose load-bearing role for cross-stream dtype alignment is not in scope for this audit (Out of Scope §).Affected Repositories
98df2309)pollockjj/ComfyUIis the deliverable: the edit tocomfy/ldm/seedvr/model.py(MMModule.forward, lines 624–625 removed) and the new regression test undertests-unit/comfy_test/test_seedvr_forward_no_device_cast.pyland onissue_185cut fromorigin/issue_101HEAD98df2309and PR back intoissue_101. This mirrors the merged eval-and-fix PR sequence #46 (Comfy-Org#192), #44 (Comfy-Org#190), #43 (Comfy-Org#194), #36 (Comfy-Org#119), #35 (Comfy-Org#188), #34 (Comfy-Org#189), #33 (#109), #32 (Comfy-Org#183), #31 (groupnorm).pollockjj/mydevelopmentis the bookkeeping target: every committed artifact undergithub_issues/185/slice1/(Phase 0 probe transcript, A/B pytest logs, AST-extracted forward source, scope-bounded diff, test source copy) lives on the outer workspace'sissue_185branch and PRs back intomainas the standard[bookkeeping] [Issue 101][eval-and-fix]pattern.Asset Readiness
git rev-parse origin/issue_101=98df23092.11.0+cu130, CUDA available98df2309)comfy.model_management.get_torch_deviceimport targetMMModule/MMArg/get_args/get_kwargssymbol contractsgithub_issues/185/slice1/writable onissue_185Research and Methodology
Plan Foundations Comment URL: https://github.com/pollockjj/mydevelopment/issues/185#issuecomment-4383764590 — Foundations comment for Comfy-Org#185 is the load-bearing reference for the inline R&M and TPM content reproduced verbatim below; every AC carrying a
Foundations-pin:line cites the comment node-id andcreatedAttimestamp (lastEditedAtis null on this comment, so qa-plan Check 21 sub-check 6 falls back tocreatedAtas the authoritative pin).Detected Scope:
none— port of the established eval-and-fix pattern to one residual cast site flagged by Kosinkadink on the upstream PR review (Comfy-Org#11294 issue-comment4259303182, item 4); existing Comfy model-management contract IS the equivalence reference, not a published metric. Equivalence rule is binary source-AST equality (zeroget_torch_device()Call nodes inside anyforwardFunctionDef body incomfy/ldm/seedvr/model.py) plus binary behavioral equality (post-fixMMModule.forward(vid, txt)runs to completion withcomfy.model_management.get_torch_devicemonkey-patched to raise, returning tensors on the same device as the inputs). No tolerance band, no statistical test, no measurement instrument.The change class is "honor an existing internal API contract" — Comfy's per-model
load_deviceplacement contract forforwardinvocations. Source authority is the live source ofcomfy/ldm/seedvr/model.pyon the issue's base branch (origin/issue_101HEAD98df2309), captured verbatim under## Ground Truth Probes. Binding parent-issue directives quoted verbatim from pollockjj/mydevelopment#185 issue body:These directives bound every AC below. Diff scope is confined to
MMModule.forwardbody (lines 613–631 on theorigin/issue_101side); the parent class structure-resolution helper and RoPE cache touched by #110 are off-limits; upstream PR Comfy-Org#11294 is not perturbed.Tools, Pipeline, and Measurements
Plan Foundations Comment URL: https://github.com/pollockjj/mydevelopment/issues/185#issuecomment-4383764590 — same Foundations comment as the R&M section above; the TPM content reproduced verbatim below is the load-bearing reference for tool / version / pipeline citations in every AC carrying a
Foundations-pin:line.Trivial scope; no external tools beyond the change itself.
pytesttests-unit/requirements.txtpin (pertests-unit/README.md)test_seedvr_groupnorm_limit.py,seedvr_model_test.pyexercise the same venv onorigin/issue_101torch2.11.0+cu130on/home/johnj/dev_cuda_1/ComfyUI/.venv/comfy.model_management.get_torch_devicecomfy/model_management.pyimport path on slot venvcomfy.ldm.seedvr.model.MMModule/MMArg/get_args/get_kwargsgitast,inspect,re,unittest.mock(viamonkeypatchfixture).venv/seedvr_model_test.pyAST pattern)Pipeline: edit one method body in one source file (
comfy/ldm/seedvr/model.py,MMModule.forward, lines 624–625 on theorigin/issue_101side, removing the two lines verbatim) → add one new test filetests-unit/comfy_test/test_seedvr_forward_no_device_cast.pydefining two pytest test functions → runpytestagainst the new test onissue_185HEAD (post-fix capture) and against the same test withcomfy/ldm/seedvr/model.pyreverted toorigin/issue_101(pre-fix capture) → commit pytest logs, AST-extracted forward source, grep transcripts, and scope-boundedgit difftogithub_issues/185/slice1/on the bookkeeping branch.Measurements: zero numeric measurements. The two evidence primitives are (a) source-AST count of
get_torch_device()Call nodes insideforwardFunctionDef bodies (binary, must equal0post-fix) and (b) behavioral pytest pass/fail on a forward invocation withget_torch_devicemonkey-patched to raise (binary, must PASS post-fix). HR-06 (single-probe-before-sweep) and HR-11 (boundary-bracketing) do not apply — no sweep, no metric distribution.Ground Truth Probes
Every literal API surface the ACs below cite is anchored to a probe captured live against the issue's read target (
pollockjj/ComfyUI:origin/issue_101HEAD98df2309) or against the active PyTorch interpreter at plan-authoring time (/home/johnj/dev_cuda_1/ComfyUI/.venv/bin/python, torch2.11.0+cu130, CUDA available).cd /home/johnj/dev_cuda_1/ComfyUI && git show origin/issue_101:comfy/ldm/seedvr/model.py | python3 -c 'import ast,sys; tree=ast.parse(sys.stdin.read()); [print(f"forward {n.lineno}-{n.end_lineno}") for n in ast.walk(tree) if isinstance(n,ast.FunctionDef) and n.name=="forward"]'forward 34-51forward 369-399forward 422-439forward 511-548forward 613-631forward 686-687forward 731-828forward 842-846forward 864-865forward 932-969forward 984-993forward 996-1018forward 1033-1043forward 1046-1064forward 1095-1145forward 1167-1189forward 1387-1471— 17forwardFunctionDefs total; theMMModule.forwardbody the AC operates on spans lines 613–631.cd /home/johnj/dev_cuda_1/ComfyUI && git show origin/issue_101:comfy/ldm/seedvr/model.py | python3 -c 'import ast,sys; t=ast.parse(sys.stdin.read()); cls={id(m):c.name for c in ast.walk(t) if isinstance(c,ast.ClassDef) for m in c.body if isinstance(m,ast.FunctionDef)}; [print(f"class={cls.get(id(n))} forward.lineno={n.lineno} call.lineno={i.lineno}") for n in ast.walk(t) if isinstance(n,ast.FunctionDef) and n.name=="forward" for i in ast.walk(n) if isinstance(i,ast.Call) and isinstance(i.func,ast.Attribute) and i.func.attr=="get_torch_device"]'class=MMModule forward.lineno=613 call.lineno=624— exactly oneget_torch_device()Call node inside anyforwardmethod body in the entire file, located insideMMModule.forwardat line 624. The post-fix expectation is zero matches.cd /home/johnj/dev_cuda_1/ComfyUI && git show origin/issue_101:comfy/ldm/seedvr/model.py | python3 -c 'import ast,sys; t=ast.parse(sys.stdin.read()); cls={id(m):c.name for c in ast.walk(t) if isinstance(c,ast.ClassDef) for m in c.body if isinstance(m,ast.FunctionDef)}; [print(f"class={cls.get(id(n))} forward.lineno={n.lineno} call.lineno={i.lineno}") for n in ast.walk(t) if isinstance(n,ast.FunctionDef) and n.name=="forward" for i in ast.walk(n) if isinstance(i,ast.Call) and isinstance(i.func,ast.Attribute) and i.func.attr=="to" and any(kw.arg=="device" for kw in i.keywords)]'class=MMModule forward.lineno=613 call.lineno=629— exactly one.to(device=...)keyword cast inside anyforwardmethod body, located at MMModule.forward line 629 (txt = txt.to(device=vid.device, dtype=vid.dtype)). Its device argument derives fromvid.device(input-stream device), NOT fromcomfy.model_management.get_torch_device(); this is reported by the Phase 0 probe as a structurally distinct pattern from the lines 624-625 deliverable scope (Out of Scope §).cd /home/johnj/dev_cuda_1/ComfyUI && git show origin/issue_101:comfy/ldm/seedvr/model.py | sed -n '613,631p'device = comfy.model_management.get_torch_device()) and 625 (vid = vid.to(device)); every other line of the body is preserved verbatim.cd /home/johnj/dev_cuda_1/ComfyUI && git ls-tree --name-only origin/issue_101 tests-unit/comfy_test/ tests-unit/comfy_extras_test/ | grep -E 'seedvr'origin/issue_101HEAD; AC-6 enumerates them all and asserts each remains green onissue_185HEAD. The new filetest_seedvr_forward_no_device_cast.pydoes not yet exist.cd /home/johnj/dev_cuda_1/ComfyUI && git show origin/issue_101:comfy/ldm/seedvr/model.py | python3 -c 'import ast,sys; t=ast.parse(sys.stdin.read()); want={"MMArg","get_args","get_kwargs","MMModule"}; [print(f"{n.name}: kind={type(n).__name__} lineno={n.lineno} end={n.end_lineno}") for n in ast.walk(t) if (isinstance(n,ast.ClassDef) or isinstance(n,ast.FunctionDef)) and n.name in want]'MMArg: kind=ClassDef lineno=154 end=156get_args: kind=FunctionDef lineno=177 end=178get_kwargs: kind=FunctionDef lineno=181 end=182MMModule: kind=ClassDef lineno=589 end=631— the four symbols the smoke test scaffold imports/instantiates exist at the cited line ranges;MMModule.__init__acceptsmodule: Callable[..., nn.Module], *args, shared_weights: bool=False, vid_only: bool=False, **kwargsand instantiatesself.vid/self.txtviamodule(*get_args("vid", args), **get_kwargs("vid", kwargs)).cd /home/johnj/dev_cuda_1/ComfyUI && /home/johnj/dev_cuda_1/ComfyUI/.venv/bin/python -c 'import comfy.model_management as m, inspect; print("callable=",callable(m.get_torch_device)); print("sig=",inspect.signature(m.get_torch_device))'callable= Truesig= ()—comfy.model_management.get_torch_deviceis a zero-arg callable on the slot venv. The smoke test monkey-patches it viamonkeypatch.setattr(comfy.model_management, "get_torch_device", boom)whereboomis a zero-arg function that increments a counter and raisesRuntimeError; pre-fix, thedevice = comfy.model_management.get_torch_device()lookup at MMModule.forward line 624 invokesboom, the counter increments, andRuntimeErrorpropagates out offorward. Post-fix, the lookup is gone and the counter remains zero.cd /home/johnj/dev_cuda_1/ComfyUI && /home/johnj/dev_cuda_1/ComfyUI/.venv/bin/python -c 'import torch; print("torch", torch.__version__); print("cuda_available", torch.cuda.is_available())'torch 2.11.0+cu130cuda_available True— slot venv is CUDA-capable; on a CUDA-host run, an unpatchedget_torch_device()returns a CUDA device, so the pre-fixvid.to(device)cast at line 625 actually moves CPU input tensors to GPU. The smoke test pins inputs totorch.device("cpu")and the monkey-patchedget_torch_deviceraises before any device move can occur, so the pre-fix failure mode is theRuntimeErrorfromboom(not a CUDA-vs-CPU device-mismatch downstream).If any literal in an AC is not anchored above, the AC is REJECTED. The slicer must replicate the exact GTP commands (byte-identical) when assembling the slice — every cited file:line, function name, class name, and Python literal in the slice's commit must match the GTP output above.
Created Surface Contract
The plan introduces the following literals; they are anchored by the slice that creates them, not by Phase 0 probes (these strings do not exist on the read target — the slicer writes them):
tests-unit/comfy_test/test_seedvr_forward_no_device_cast.pypollockjj/ComfyUIonissue_185. Defines exactly two pytest test functions: (1)test_no_get_torch_device_in_forward_methods— usesinspect.getsource(comfy.ldm.seedvr.model)thenast.parse(...)and asserts that the list[(n.lineno, i.lineno) for n in ast.walk(tree) if isinstance(n, ast.FunctionDef) and n.name == "forward" for i in ast.walk(n) if isinstance(i, ast.Call) and isinstance(i.func, ast.Attribute) and i.func.attr == "get_torch_device"]is empty. (2)test_mmmodule_forward_succeeds_without_get_torch_device_lookup— uses themonkeypatchfixture to replacecomfy.model_management.get_torch_devicewith a zero-arg callable that incrementscall_count[0]and raisesRuntimeError("MMModule.forward called get_torch_device()"); constructsMMModule(_IdentityCallable, shared_weights=False, vid_only=False)where_IdentityCallableis a localnn.Modulesubclass whoseforward(x, *args, **kwargs)returnsx; callsmm.forward(torch.zeros(2, 4), torch.ones(2, 4)); asserts (a)call_count[0] == 0, (b)torch.equal(vid_out, vid_in), (c)torch.equal(txt_out, txt_in), (d)vid_out.device == vid_in.device, (e)txt_out.device == txt_in.device. The test setscomfy.cli_args.args.cpu = Truewhennot torch.cuda.is_available()(matches thetest_seedvr_groupnorm_limit.pyimport preamble).github_issues/185/slice1/phase0_probe.logpollockjj/mydevelopmentissue_185. Verbatim stdout of the two AST probes (GTP-2 and GTP-3) captured againstorigin/issue_101HEAD98df2309before any slice edits. Final file content matches GTP-2'sclass=MMModule forward.lineno=613 call.lineno=624line and GTP-3'sclass=MMModule forward.lineno=613 call.lineno=629line, with a header recording the read-target SHA.github_issues/185/slice1/pytest_post_fix.logpollockjj/mydevelopmentissue_185. Verbatim stdout/stderr ofcd /home/johnj/dev_cuda_1/ComfyUI && python3 -m pytest tests-unit/comfy_test/test_seedvr_forward_no_device_cast.py -vrun onissue_185HEAD. Final non-blank summary line ends with2 passed.github_issues/185/slice1/pytest_pre_fix.logpollockjj/mydevelopmentissue_185. Verbatim stdout/stderr of the same pytest invocation captured aftercd /home/johnj/dev_cuda_1/ComfyUI && git checkout origin/issue_101 -- comfy/ldm/seedvr/model.pyis applied to the deliverable working tree (the new test file fromissue_185HEAD remains in place; onlymodel.pyis reverted). Captures oneassert not foundfailure ontest_no_get_torch_device_in_forward_methodsreporting[(613, 624)]plus oneRuntimeError: MMModule.forward called get_torch_device()failure ontest_mmmodule_forward_succeeds_without_get_torch_device_lookup. The deliverable working tree is restored toissue_185HEAD viacd /home/johnj/dev_cuda_1/ComfyUI && git restore --source=HEAD comfy/ldm/seedvr/model.pyimmediately after capture, before any commit on the deliverable.github_issues/185/slice1/forward_source.txtpollockjj/mydevelopmentissue_185. Verbatim AST-extracted source ofMMModule.forwardfromissue_185HEADcomfy/ldm/seedvr/model.py, produced bycd /home/johnj/dev_cuda_1/ComfyUI && python3 -c 'import ast; src=open("comfy/ldm/seedvr/model.py").read(); tree=ast.parse(src); cls=[n for n in ast.walk(tree) if isinstance(n,ast.ClassDef) and n.name=="MMModule"][0]; m=[n for n in cls.body if isinstance(n,ast.FunctionDef) and n.name=="forward"][0]; print(ast.get_source_segment(src,m))'. The file content shows zero occurrences of the substringscomfy.model_management.get_torch_deviceandvid.to(device).github_issues/185/slice1/forbidden_call_grep.logpollockjj/mydevelopmentissue_185. Output ofgrep -n -E 'comfy\.model_management\.get_torch_device|vid = vid\.to\(device\)' github_issues/185/slice1/forward_source.txt; echo "exit=$?". The committed log recordsexit=1and zero matching lines.github_issues/185/slice1/scope_diff.txtpollockjj/mydevelopmentissue_185. Output ofcd /home/johnj/dev_cuda_1/ComfyUI && git diff origin/issue_101..issue_185 -- comfy/ldm/seedvr/model.py. The diff shows two deleted lines (the verbatim text of original lines 624 and 625 from GTP-4) and zero hunks outside the body ofMMModule.forward(lines 613–631 on theorigin/issue_101side). The artifact contains the verbatim diff plus a final stanza listing every@@-hunk header range and confirming each falls inside[613, 631]on the-side.github_issues/185/slice1/test_source.txtpollockjj/mydevelopmentissue_185. Verbatim copy produced bycp /home/johnj/dev_cuda_1/ComfyUI/tests-unit/comfy_test/test_seedvr_forward_no_device_cast.py github_issues/185/slice1/test_source.txt. AC-5 verifies the file defines exactly the two named test functions, the AST assertion shape, and the monkeypatch boom-counter assertion shape.github_issues/185/slice1/companion_pytest.logpollockjj/mydevelopmentissue_185. Verbatim stdout/stderr ofcd /home/johnj/dev_cuda_1/ComfyUI && python3 -m pytest tests-unit/comfy_test/seedvr_model_test.py tests-unit/comfy_test/seedvr_vae_forward_test.py tests-unit/comfy_test/seedvr_vae_wrapper_forward_test.py tests-unit/comfy_test/test_seedvr_groupnorm_limit.py tests-unit/comfy_test/test_seedvr_rope_delegation.py tests-unit/comfy_test/test_seedvr_vae_decode_batch_axes.py tests-unit/comfy_test/test_seedvr_vae_decode_guards.py tests-unit/comfy_test/test_seedvr_vae_decode_unpadded_t.py tests-unit/comfy_test/test_seedvr_vae_loader_metadata.py tests-unit/comfy_test/test_seedvr_vae_tiled_args_no_mutate.py tests-unit/comfy_extras_test/test_seedvr_conditioning_hardening.py tests-unit/comfy_extras_test/test_seedvr_node_signature.py -vrun onissue_185HEAD. Final non-blank summary line ends withpassedand zerofailed/ zeroerrors. The 12 file paths enumerated in the command match GTP-5 verbatim.Slices
Slice 1: Remove residual
get_torch_device()cast fromMMModule.forward+ lock invariant + smoke testKind: implementation
Objective: Prove that
MMModule.forward()no longer readscomfy.model_management.get_torch_device()nor forcesvid = vid.to(device), that the change is scoped strictly to the body ofMMModule.forward(no other method body, no other class touched), that the repository-level invariant againstget_torch_device()calls inside anyforwardmethod body incomfy/ldm/seedvr/model.pyis locked in by an AST-walked regression, that annn.Identity-shaped smoke throughMMModule.forward(vid, txt)succeeds without invokingget_torch_device(verified viamonkeypatchthat raises if invoked) and preserves input device placement, and that the existing 12-test SeedVR2 surface remains pytest-green onissue_185HEAD after the edit lands.Acceptance Criteria
cd /home/johnj/dev_cuda_1/ComfyUI && python3 -m pytest tests-unit/comfy_test/test_seedvr_forward_no_device_cast.py -vexits 0 with2 passedin the summary line — verified by committed artifactgithub_issues/185/slice1/pytest_post_fix.logwhose last non-blank line ends with2 passed.origin/issue_101source raisesassert not foundontest_no_get_torch_device_in_forward_methods(reporting[(613, 624)]per GTP-2) andRuntimeError: MMModule.forward called get_torch_device()ontest_mmmodule_forward_succeeds_without_get_torch_device_lookup(per GTP-4 line 624 and GTP-7'sboomsemantics)2 passedMMModule.forward()no longer readscomfy.model_management.get_torch_device()nor forcesvid = vid.to(device), that the change is scoped strictly to the body ofMMModule.forward(no other method body, no other class touched), that the repository-level invariant againstget_torch_device()calls inside anyforwardmethod body incomfy/ldm/seedvr/model.pyis locked in by an AST-walked regression, that annn.Identity-shaped smoke throughMMModule.forward(vid, txt)succeeds without invokingget_torch_device(verified viamonkeypatchthat raises if invoked) and preserves input device placement, and that the existing 12-test SeedVR2 surface remains pytest-green onissue_185HEAD after the edit lands."tests-unit/comfy_test/test_seedvr_forward_no_device_cast.py -vproves the same observables flip across the fix:github_issues/185/slice1/pytest_pre_fix.logrecords theassert not foundfailure ontest_no_get_torch_device_in_forward_methods(reporting[(613, 624)]) plus theRuntimeError: MMModule.forward called get_torch_device()failure ontest_mmmodule_forward_succeeds_without_get_torch_device_lookupaftercd /home/johnj/dev_cuda_1/ComfyUI && git checkout origin/issue_101 -- comfy/ldm/seedvr/model.py, andgithub_issues/185/slice1/pytest_post_fix.logrecords pytest exit 0 with2 passedaftercd /home/johnj/dev_cuda_1/ComfyUI && git restore --source=HEAD comfy/ldm/seedvr/model.pyrestoresissue_185HEAD.comfy.model_management.get_torch_deviceis the zero-arg callable that the monkey-patchedboomreplaces.issue_185HEAD exits 0 with2 passedand zeroassert not found/RuntimeErrorfailures.MMModule.forward()no longer readscomfy.model_management.get_torch_device()nor forcesvid = vid.to(device), that the change is scoped strictly to the body ofMMModule.forward(no other method body, no other class touched), that the repository-level invariant againstget_torch_device()calls inside anyforwardmethod body incomfy/ldm/seedvr/model.pyis locked in by an AST-walked regression, that annn.Identity-shaped smoke throughMMModule.forward(vid, txt)succeeds without invokingget_torch_device(verified viamonkeypatchthat raises if invoked) and preserves input device placement, and that the existing 12-test SeedVR2 surface remains pytest-green onissue_185HEAD after the edit lands."MMModule.forwardonissue_185HEAD contains zero occurrences of the substringscomfy.model_management.get_torch_deviceandvid = vid.to(device)— verified by committed artifactsgithub_issues/185/slice1/forward_source.txt(the extracted body) andgithub_issues/185/slice1/forbidden_call_grep.log(grep -n -E '...' forward_source.txt; echo "exit=$?"recordsexit=1, zero matching lines). The remaining body lines from GTP-4 (the four-linedef forward(...)header through theTuple[...]return type, thevid_module = ...line, thevid = vid_module(vid, ...)line, theif not self.vid_only:block including line 629'stxt = txt.to(device=vid.device, dtype=vid.dtype)per GTP-3, and the finalreturn vid, txt) are preserved verbatim. Note: GTP-3's.to(device=vid.device, dtype=vid.dtype)cast at line 629 is not removed by this slice; it is a derived-device cast (Out of Scope §) and is reported by Phase 0 for completeness only.comfy.model_management.get_torch_deviceat line 624 andvid = vid.to(device)at line 625 of the pre-fix forward body.github_issues/185/slice1/forward_source.txtshows neither substring;github_issues/185/slice1/forbidden_call_grep.logrecords exit code 1 with zero matches.MMModule.forward()no longer readscomfy.model_management.get_torch_device()nor forcesvid = vid.to(device), that the change is scoped strictly to the body ofMMModule.forward(no other method body, no other class touched), that the repository-level invariant againstget_torch_device()calls inside anyforwardmethod body incomfy/ldm/seedvr/model.pyis locked in by an AST-walked regression, that annn.Identity-shaped smoke throughMMModule.forward(vid, txt)succeeds without invokingget_torch_device(verified viamonkeypatchthat raises if invoked) and preserves input device placement, and that the existing 12-test SeedVR2 surface remains pytest-green onissue_185HEAD after the edit lands."cd /home/johnj/dev_cuda_1/ComfyUI && git diff origin/issue_101..issue_185 -- comfy/ldm/seedvr/model.pyproduces hunks confined to the line range ofMMModule.forwardon theorigin/issue_101side (lines 613–631 per GTP-1) and zero hunks touching any of the other 16forwardmethods enumerated by GTP-1 (forward 34-51,369-399,422-439,511-548,686-687,731-828,842-846,864-865,932-969,984-993,996-1018,1033-1043,1046-1064,1095-1145,1167-1189,1387-1471) or any other class definition in the file — verified by committed artifactgithub_issues/185/slice1/scope_diff.txt. The diff shows exactly two deleted lines (text matching original lines 624 and 625 from GTP-4) and zero added lines. The artifact contains the verbatim diff plus a final stanza listing every@@-hunk header range and confirming each falls inside[613, 631]on the-side.forwardmethods; GTP-2 localizes the onlyget_torch_device()call site to MMModule.forward line 624. Any diff hunk outside[613, 631]is extraneous to the diagnosed defect.github_issues/185/slice1/scope_diff.txtenumerates only in-range hunks with two deletions and zero additions.MMModule.forward()no longer readscomfy.model_management.get_torch_device()nor forcesvid = vid.to(device), that the change is scoped strictly to the body ofMMModule.forward(no other method body, no other class touched), that the repository-level invariant againstget_torch_device()calls inside anyforwardmethod body incomfy/ldm/seedvr/model.pyis locked in by an AST-walked regression, that annn.Identity-shaped smoke throughMMModule.forward(vid, txt)succeeds without invokingget_torch_device(verified viamonkeypatchthat raises if invoked) and preserves input device placement, and that the existing 12-test SeedVR2 surface remains pytest-green onissue_185HEAD after the edit lands."tests-unit/comfy_test/test_seedvr_forward_no_device_cast.pyonissue_185defines exactly two pytest test functions whose names aretest_no_get_torch_device_in_forward_methodsandtest_mmmodule_forward_succeeds_without_get_torch_device_lookup. The first runstree = ast.parse(inspect.getsource(comfy.ldm.seedvr.model))and asserts[(n.lineno, i.lineno) for n in ast.walk(tree) if isinstance(n, ast.FunctionDef) and n.name == "forward" for i in ast.walk(n) if isinstance(i, ast.Call) and isinstance(i.func, ast.Attribute) and i.func.attr == "get_torch_device"] == []. The second usesmonkeypatch.setattr(comfy.model_management, "get_torch_device", boom)whereboomis a zero-arg callable that incrementscall_count[0]and raisesRuntimeError, instantiatesMMModule(_IdentityCallable, shared_weights=False, vid_only=False)with a local_IdentityCallable(nn.Module)whoseforward(x, *args, **kwargs)returnsx, invokesmm.forward(torch.zeros(2, 4), torch.ones(2, 4)), and asserts (a)call_count[0] == 0, (b)torch.equal(vid_out, vid_in), (c)torch.equal(txt_out, txt_in), (d)vid_out.device == vid_in.device, (e)txt_out.device == txt_in.device— verified by committed artifactgithub_issues/185/slice1/test_source.txt(cp /home/johnj/dev_cuda_1/ComfyUI/tests-unit/comfy_test/test_seedvr_forward_no_device_cast.py github_issues/185/slice1/test_source.txt) plus the AC-1 pytest log showing both test names in thePASSEDlines.test_seedvr_forward_no_device_cast.pydoes not exist onorigin/issue_101HEAD.github_issues/185/slice1/test_source.txtadds the two named tests andgithub_issues/185/slice1/pytest_post_fix.logshows both PASS.MMModule.forward()no longer readscomfy.model_management.get_torch_device()nor forcesvid = vid.to(device), that the change is scoped strictly to the body ofMMModule.forward(no other method body, no other class touched), that the repository-level invariant againstget_torch_device()calls inside anyforwardmethod body incomfy/ldm/seedvr/model.pyis locked in by an AST-walked regression, that annn.Identity-shaped smoke throughMMModule.forward(vid, txt)succeeds without invokingget_torch_device(verified viamonkeypatchthat raises if invoked) and preserves input device placement, and that the existing 12-test SeedVR2 surface remains pytest-green onissue_185HEAD after the edit lands."cd /home/johnj/dev_cuda_1/ComfyUI && python3 -m pytest tests-unit/comfy_test/seedvr_model_test.py tests-unit/comfy_test/seedvr_vae_forward_test.py tests-unit/comfy_test/seedvr_vae_wrapper_forward_test.py tests-unit/comfy_test/test_seedvr_groupnorm_limit.py tests-unit/comfy_test/test_seedvr_rope_delegation.py tests-unit/comfy_test/test_seedvr_vae_decode_batch_axes.py tests-unit/comfy_test/test_seedvr_vae_decode_guards.py tests-unit/comfy_test/test_seedvr_vae_decode_unpadded_t.py tests-unit/comfy_test/test_seedvr_vae_loader_metadata.py tests-unit/comfy_test/test_seedvr_vae_tiled_args_no_mutate.py tests-unit/comfy_extras_test/test_seedvr_conditioning_hardening.py tests-unit/comfy_extras_test/test_seedvr_node_signature.py -vexits 0 onissue_185HEAD with the final summary line containingpassedand zerofailed/ zeroerrors— verified by committed artifactgithub_issues/185/slice1/companion_pytest.log. The 12 file paths in the command match GTP-5 verbatim.github_issues/185/slice1/pytest_pre_fix.logrecords the dedicated failing behavior under audit on the pre-fix tree:assert not foundwith[(613, 624)]plusRuntimeError: MMModule.forward called get_torch_device(), while the 12 legacy SeedVR2 tests listed in GTP-5 are already green onorigin/issue_101.github_issues/185/slice1/pytest_post_fix.logflips the dedicated new test to2 passed, andgithub_issues/185/slice1/companion_pytest.logshows the 12 legacy SeedVR2 tests remain green with zerofailed/ zeroerrors.MMModule.forward()no longer readscomfy.model_management.get_torch_device()nor forcesvid = vid.to(device), that the change is scoped strictly to the body ofMMModule.forward(no other method body, no other class touched), that the repository-level invariant againstget_torch_device()calls inside anyforwardmethod body incomfy/ldm/seedvr/model.pyis locked in by an AST-walked regression, that annn.Identity-shaped smoke throughMMModule.forward(vid, txt)succeeds without invokingget_torch_device(verified viamonkeypatchthat raises if invoked) and preserves input device placement, and that the existing 12-test SeedVR2 surface remains pytest-green onissue_185HEAD after the edit lands."Constraints
python3python3 -m pytest <test-path> -vinvoked from the deliverable checkout root/home/johnj/dev_cuda_1/ComfyUI.venv/already providestorch 2.11.0+cu130andpytestper GTP-8 and the slot layout in CLAUDE.mdpkill, norm -rf, nopython main.py, no/tmpwrites&&/||/;chainingpollockjj/ComfyUI:issue_185cut fromorigin/issue_101(HEAD98df2309); PR target =issue_101github_issues/185/slice1/*) land onpollockjj/mydevelopment:issue_185cut frommain; PR target =main(standard[bookkeeping] [Issue 101][eval-and-fix]pattern)MMModule.forwardincomfy/ldm/seedvr/model.py; the other 16forwardmethods enumerated by GTP-1 are off-limits, as areMMModule.__init__, every other class in the file (CustomRMSNorm,MMArg,RotaryEmbeddingBase,MMRotaryEmbeddingBase,NaMMRotaryEmbedding3d,NaMMAttention,AdaLayerNorm,TimeEmbedding,NaDiT, etc.), and every module-level function (safe_pad_operation,get_args,get_kwargs,get_window_op, etc.)comfy/ldm/seedvr/vae.py,comfy_extras/nodes_seedvr.py,comfy/sd.py, or any other file in the deliverable repoMMModule.forward's signature or return-type annotation (thedef forward(self, vid, txt, *args, **kwargs) -> Tuple[torch.FloatTensor, torch.FloatTensor]:shape from GTP-4 lines 613–622 is preserved verbatim)Comfy-Org/ComfyUI#11294oryousef-rafat:seedvr2pytestfrom the deliverable slot's.venv/— REUSE; no installcomfy.ldm.seedvr.model.MMModule/MMArg/get_args/get_kwargsimport paths — REUSE; verified by GTP-6comfy.model_management.get_torch_device— REUSE; verified by GTP-7comfy.cli_args.args.cpu = Trueimport-time guard pattern — REUSE; established intests-unit/comfy_test/test_seedvr_groupnorm_limit.pyandtests-unit/comfy_test/seedvr_model_test.pygit,grep, AST extraction via stdlibastmodule,monkeypatchfixture — REUSEgithub_issues/185/slice1/are the only added paths in the bookkeeping repoMMModule.forwardstops callingget_torch_device()) → A/B pytest pair: post-fix log onissue_185HEAD plus pre-fix log captured withmodel.pytransiently reverted toorigin/issue_101(Slice 1 AC-1, AC-2)comfy.model_management.get_torch_deviceand novid = vid.to(device)in MMModule.forward body) → AST-extracted committed source plus zero-match grep transcript (Slice 1 AC-3)git diff origin/issue_101..issue_185 -- comfy/ldm/seedvr/model.pywith hunk-range enumeration (Slice 1 AC-4)git stashor external decision deferral..gitignorerules for safe generated bulk, or discarded generated debris.issue_185(e.g. the?? github_issues/185/dispatch artifacts seen at plan time on the bookkeeping checkout) is repo provenance and is integrated, not removed as dirty-state cleanup.git status --shortgates locally on both/home/johnj/dev_cuda_1/ComfyUIand/home/johnj/dev_cuda_1/mydevelopmentand must refuse to submit while either is dirty.git checkout origin/issue_101 -- comfy/ldm/seedvr/model.py) MUST be undone viagit restore --source=HEAD comfy/ldm/seedvr/model.pybefore any commit is created on the deliverable; the pre-fix log itself is committed only to the bookkeeping repo.Out of Scope
comfy/ldm/seedvr/model.py:MMModule.forward:629txt = txt.to(device=vid.device, dtype=vid.dtype)— Phase 0 GTP-3 reports this as the only.to(device=...)keyword-form cast inside anyforwardmethod body in the file. Its device argument isvid.device(input-stream-derived), notcomfy.model_management.get_torch_device(); it is structurally distinct from Kosinkadink's flagged pattern (get_torch_device()lookup →.to(device)cast on the looked-up value). The issue body explicitly scopes the deliverable to "the remaining cast atMMModule.forward(lines 624-625)" — GTP-2's single match — not to every.to(device=...)call site. Line 629 also carries adtype=vid.dtypecomponent whose load-bearing role for cross-stream dtype alignment is a separate audit question that this plan does not adjudicate. Out of scope for this issue; if a follow-up audit determines line 629 is also a violation, file a separate issue.forwardmethods enumerated by GTP-1 (forward 34-51,369-399,422-439,511-548,686-687,731-828,842-846,864-865,932-969,984-993,996-1018,1033-1043,1046-1064,1095-1145,1167-1189,1387-1471) — Phase 0 GTP-2 confirms zeroget_torch_device()calls in any of them. They are off-limits to this slice.comfy/ldm/seedvr/vae.py,comfy_extras/nodes_seedvr.py,comfy/sd.py— explicitly out of scope per the issue body's Non-Goals.comfy/ldm/seedvr/model.pybut at different sites (structure-resolution helper, RoPE cache); coordinate at merge if both land same day. The two issues edit non-overlapping regions of the same file: select image from list node / or modify SaveImage Comfy-Org/ComfyUI#185 edits MMModule.forward (lines 613–631), Add SeedVR2 support #110 edits the structure-resolution helper and the RoPE cache (different methods per the issue body cross-reference). Neither blocks the other on different methods; the slicer'sgit diff(AC-4) confines hunks to[613, 631]precisely so the merge can be linear.mergeable=DIRTYand silent for two weeks per the issue body; pushing fixes back toyousef-rafat:seedvr2from this issue is out of scope and requires a separate decision.ops,load_device" comment is context, not mandate). The slice removes two lines and adds one test file; it does not propagateload_devicethrough MMModule's constructor or rewire weight placement.MMModule.forwardagainst annn.Identity-shaped stub (_IdentityCallable.forward(x)returnsx); correctness of real SeedVR2 transformer math is covered by the existing seedvr workflow integration onissue_101and is not re-validated by this slice.call_count[0] == 0,torch.equal(...),vid_out.device == vid_in.device, AST[] == []).MMModule.forwardbeyond removing the two cast lines (624–625). The four-linedef forward(...)header through theTuple[...]return type,vid_module = ...,vid = vid_module(vid, ...), theif not self.vid_only:block (including line 629's derived-device cast), and thereturn vid, txtare preserved verbatim.Tracked in pollockjj/mydevelopment Comfy-Org#185 (bookkeeping PR pollockjj/mydevelopment#214).