Skip to content

refactor(run): extract shared queued tail + state-init + load helpers (BE-3265)#526

Open
mattmillerai wants to merge 1 commit into
mainfrom
matt/be-3265-dedup-run-queued-tail
Open

refactor(run): extract shared queued tail + state-init + load helpers (BE-3265)#526
mattmillerai wants to merge 1 commit into
mainfrom
matt/be-3265-dedup-run-queued-tail

Conversation

@mattmillerai

@mattmillerai mattmillerai commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

ELI-5

comfy run has two doors — one for your local ComfyUI server, one for Comfy
Cloud. 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. Three
small module-level helpers replace the duplication:

  • _load_workflow_or_exit() — the byte-identical preloaded / _load_workflow_file
    prefix that opens both execute() and execute_cloud().
  • _init_queued_state()jobs_state.new → stash item_mapwrite,
    reused by the local async path, the cloud non-wait path, and (the third copy)
    the cloud --wait path.
  • _emit_queued() — the queued tail: pretty status panel + watcher-failure
    warning + queued envelope + 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 queued envelope is a public agent-mode contract, so field membership is
kept verbatim per target — the call site owns the field set, _emit_queued
just renders it:

  • localhost, port, state_file, watcher_spawned (in that order)
  • cloudbase_url, state_file (no watcher_spawned, by design)

Other deliberately-preserved divergences: _journal_run still fires earlier in
the local path and inside the block in the cloud path; the watch hint keeps its
--where cloud suffix on cloud; _spawn_watcher still gets host/port only on
local. Field order was preserved exactly, not just the set.

Tests

  • Added two golden-envelope tests (test_run_json.py) that pin the exact
    queued field set for local and cloud, so the shared helper can never
    silently drop or add a key.
  • Full suite green: 2575 passed, 37 skipped. The run suites
    (test_run.py + test_run_json.py) are 181 passed. ruff check +
    ruff format --check clean.

Judgment call — _prepare_workflow scoped down (please note)

The ticket also proposed a _prepare_workflow() over the full
load/convert/classify/dry-run/preflight prefix. I extracted only the genuinely
identical part of that prefix (_load_workflow_or_exit) and consciously did
not force a single shared helper over the rest, because the two prefixes are
parallel-but-divergent, not verbatim:

  • object_info source differs (live server fetch_object_info vs cloud snapshot
    _load_from_target(mode="cloud"));
  • UI-conversion error codes differ (cloud adds cql_no_graph; hint text differs);
  • classify semantics differ (local splits empty vs invalid; cloud collapses
    both to workflow_not_api_format, and pops compose_meta unconditionally);
  • print_prompt differs (local return + emits preview only in the earlier
    prompt_preview event; cloud emits its own prompt_preview event then
    raise typer.Exit(0));
  • partner-node credential preflight is local-only.

A unifying helper would be a where-branching tree that's larger and riskier
than 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

  • No capability is denied and no dead-end/throw path is added — this is a pure
    extraction (negative-claim falsification trigger does not fire).
  • The local --wait completed path was intentionally left untouched: it
    writes 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.lock is in this diff

The lockfile hunk is additive-only and unrelated to the refactor itself, so
to save a reviewer the double-take: #490 (BE-2309) added the
optional-dependencies.bench = [ "anthropic>=0.40" ] extra to pyproject.toml
but never re-locked, leaving main's lockfile stale — uv lock --check fails on
main today. This branch carries the regenerated lock, which adds only the
anthropic dependency tree (anthropic, pydantic, pydantic-core, jiter,
annotated-types, docstring-parser, typing-inspection). No existing package
or version is touched, and every added package sits under the optional bench
extra.

This has no effect on CI, which installs via pip install -e . and never
reads uv.lock. It only repairs uv lock --check / uv sync --frozen for local
dev. 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 test
file, and the lockfile):

The run suites for this PR are green: test_run.py + test_run_json.py =
181 passed. ruff format --check clean.

… (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.
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 52 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0aebc8e0-966d-4fbc-99d1-d8dab3d1780d

📥 Commits

Reviewing files that changed from the base of the PR and between a732f5c and 6e2b56a.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • comfy_cli/command/run/__init__.py
  • tests/comfy_cli/command/test_run_json.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch matt/be-3265-dedup-run-queued-tail
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch matt/be-3265-dedup-run-queued-tail

Comment @coderabbitai help to get the list of available commands.

@mattmillerai mattmillerai added the agent-coded PR authored by the agent-work loop label Jul 17, 2026
@mattmillerai
mattmillerai marked this pull request as ready for review July 17, 2026 05:36
@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Jul 17, 2026
@mattmillerai mattmillerai added the cursor-review Request Cursor bot review label Jul 17, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

@mattmillerai

Copy link
Copy Markdown
Collaborator Author

Triage pass — no code changes were needed; both red checks are external to this diff.

Merge conflict: none, PR is MERGEABLE against main.

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 build once #533 lands and this branch rebases; the Windows check is red fleet-wide and not a signal on this PR.

Self-review: re-read the extraction against all three call sites. Order of operations is preserved in each path (state write → _spawn_watcher → panel → emit → tail; _journal_run still fires earlier on local and inside the block on cloud), and the queued envelope field set and order stay verbatim per target — now pinned by the two new golden tests. _emit_queued re-fetches the renderer via get_renderer() rather than taking the callers; that is the same module-level singleton both execute() and execute_cloud() already obtain from, so it is the identical object. test_run.py + test_run_json.py = 181 passed; ruff format --check clean (the 14 local UP038 hits are pre-existing in unrelated files under a newer local ruff than CIs).

One note for the reviewer: the 287-line uv.lock hunk is additive-only and not part of the refactor — #490 added the bench / anthropic>=0.40 extra without re-locking, so uv lock --check currently fails on main. This branch carries the regenerated lock (adds only the anthropic tree, touches no existing package or version). It has no effect on CI, which never reads uv.lock. I have documented this in the PR description; happy to split it out if preferred.

@mattmillerai

Copy link
Copy Markdown
Collaborator Author

🤖 The reviews loop filed Linear follow-up ticket(s) for review thread(s) deferred as out of scope for this PR:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent-coded PR authored by the agent-work loop cursor-review Request Cursor bot review size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant