refactor(run): extract shared queued tail + state-init + load helpers (BE-3265)#526
refactor(run): extract shared queued tail + state-init + load helpers (BE-3265)#526mattmillerai wants to merge 1 commit into
Conversation
… (BE-3265)
execute() and execute_cloud() carried near-verbatim twins of the ~45-line
async-submit "queued" tail (jobs_state.new → item_map → write → _spawn_watcher
→ status_glyph('queued') panel → watcher-failure warning → queued envelope →
_tail_state_file), and the cloud --wait path repeated the state-init sub-block
a third time. The load/preloaded prefix was also byte-identical.
Extract three module-level helpers, keeping each function's own submit/wait
logic and the per-target envelope contract intact:
- _load_workflow_or_exit(): the identical preloaded/_load_workflow_file prefix.
- _init_queued_state(): jobs_state.new + item_map + write, reused by the local
async path, the cloud non-wait path, and the cloud --wait path.
- _emit_queued(): the pretty panel + queued envelope + live tail. Divergent
bits stay at the call sites via params (watch_command, extra_envelope_fields)
so the public agent-mode JSON envelope is preserved verbatim per target —
local keeps host/port + watcher_spawned; cloud keeps base_url, no
watcher_spawned; _journal_run placement unchanged (local earlier, cloud in
the block).
Add two golden-envelope tests pinning the exact queued field set for local and
cloud so the shared helper can never silently drop/add a key.
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 52 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Comment |
There was a problem hiding this comment.
🔍 Cursor Review — Consolidated panel
Triggered by @mattmillerai.
✅ No high-signal findings.
Panel: 6/8 reviewers contributed findings.
Reviewers that did not contribute: kimi-k2.5:adversarial (empty), kimi-k2.5:edge-case (empty)
|
Triage pass — no code changes were needed; both red checks are external to this diff. Merge conflict: none, PR is Review feedback: nothing outstanding. The Cursor panel returned "✅ No high-signal findings" (6/8 reviewers contributed), and there are zero open review threads. CodeRabbit did not review (label-gated: "Review skipped: no matching review label", plus a rate limit). CI — both failures are pre-existing and reproduce on unrelated branches:
So this PR should go green on Self-review: re-read the extraction against all three call sites. Order of operations is preserved in each path (state write → One note for the reviewer: the 287-line |
|
🤖 The reviews loop filed Linear follow-up ticket(s) for review thread(s) deferred as out of scope for this PR:
|
ELI-5
comfy runhas two doors — one for your local ComfyUI server, one for ComfyCloud. Both doors did the exact same thing at the end after handing off a job:
write a little status file, start a background watcher, print the pretty "⏳
queued" panel, emit the machine-readable JSON line, and briefly tail the job.
That ~45-line "queued tail" was copy-pasted verbatim in both, and the cloud path
even repeated part of it a third time. Copy-paste drift had already started (the
two JSON envelopes had quietly diverged). This PR moves that shared tail into one
helper so there's a single place to change it, without altering what any user or
agent actually sees.
What changed
Pure refactor of
comfy_cli/command/run/__init__.py— no behavior change. Threesmall module-level helpers replace the duplication:
_load_workflow_or_exit()— the byte-identicalpreloaded/_load_workflow_fileprefix that opens both
execute()andexecute_cloud()._init_queued_state()—jobs_state.new→ stashitem_map→write,reused by the local async path, the cloud non-wait path, and (the third copy)
the cloud
--waitpath._emit_queued()— the queued tail: pretty status panel + watcher-failurewarning +
queuedenvelope + live state tail.Each function keeps its own submit/wait logic; only the shared mechanics moved.
Preserving the public JSON envelope (the careful part)
The
queuedenvelope is a public agent-mode contract, so field membership iskept verbatim per target — the call site owns the field set,
_emit_queuedjust renders it:
host,port,state_file,watcher_spawned(in that order)base_url,state_file(nowatcher_spawned, by design)Other deliberately-preserved divergences:
_journal_runstill fires earlier inthe local path and inside the block in the cloud path; the watch hint keeps its
--where cloudsuffix on cloud;_spawn_watcherstill getshost/portonly onlocal. Field order was preserved exactly, not just the set.
Tests
test_run_json.py) that pin the exactqueuedfield set for local and cloud, so the shared helper can neversilently drop or add a key.
runsuites(
test_run.py+test_run_json.py) are 181 passed.ruff check+ruff format --checkclean.Judgment call —
_prepare_workflowscoped down (please note)The ticket also proposed a
_prepare_workflow()over the fullload/convert/classify/dry-run/preflight prefix. I extracted only the genuinely
identical part of that prefix (
_load_workflow_or_exit) and consciously didnot force a single shared helper over the rest, because the two prefixes are
parallel-but-divergent, not verbatim:
fetch_object_infovs cloud snapshot_load_from_target(mode="cloud"));cql_no_graph; hint text differs);emptyvsinvalid; cloud collapsesboth to
workflow_not_api_format, and popscompose_metaunconditionally);print_promptdiffers (localreturn+ emits preview only in the earlierprompt_previewevent; cloud emits its ownprompt_previewevent thenraise typer.Exit(0));A unifying helper would be a
where-branching tree that's larger and riskierthan the duplication it removes, against the "small, conservative" guardrail. The
headline of the ticket — the verbatim ~45-line queued tail — is fully extracted;
the prefix is addressed for its truly-shared slice. Happy to take the fuller
prefix extraction as a follow-up if a reviewer prefers it.
Self-review
extraction (negative-claim falsification trigger does not fire).
--waitcompleted path was intentionally left untouched: itwrites state once as
completed, so routing it through_init_queued_state(which writes once as
queued) would add a second write — a behavior change.Why
uv.lockis in this diffThe lockfile hunk is additive-only and unrelated to the refactor itself, so
to save a reviewer the double-take:
#490(BE-2309) added theoptional-dependencies.bench = [ "anthropic>=0.40" ]extra topyproject.tomlbut never re-locked, leaving
main's lockfile stale —uv lock --checkfails onmaintoday. This branch carries the regenerated lock, which adds only theanthropicdependency tree (anthropic,pydantic,pydantic-core,jiter,annotated-types,docstring-parser,typing-inspection). No existing packageor version is touched, and every added package sits under the optional
benchextra.
This has no effect on CI, which installs via
pip install -e .and neverreads
uv.lock. It only repairsuv lock --check/uv sync --frozenfor localdev. Happy to split it into its own PR if a reviewer prefers.
CI status — both red checks are pre-existing and unrelated
Neither failure is caused by this diff (which touches
run/__init__.py, one testfile, and the lockfile):
build/ "Run pytest" — 9 failures intest_config_parser.py/test_node_init.py, allValueError: Comment cannot contain line breaks.tomlkitis unpinned inpyproject.tomland CI resolves it fresh; tomlkit0.15.1now rejects multi-line comments. Fixed by fix(registry): emit pyproject hint blocks as single-line tomlkit comments #533(
fix(registry): emit pyproject hint blocks as single-line tomlkit comments);this PR goes green once fix(registry): emit pyproject hint blocks as single-line tomlkit comments #533 lands and this branch rebases. Locally, against
the locked
tomlkit 0.13.3, those 83 tests pass.test/ "Windows Specific Commands" —ImportError: cannot import name '__version__' from 'pydantic_core'during the ComfyUI install step. Failing onevery open branch (including fix(registry): emit pyproject hint blocks as single-line tomlkit comments #533); repo-wide, untracked. Filed as a
follow-up.
The
runsuites for this PR are green:test_run.py+test_run_json.py=181 passed.
ruff format --checkclean.