Skip to content

fix(jobs): scope jobs ls state-file rows to the resolved --where target (add --all) - #582

Open
mattmillerai wants to merge 2 commits into
mainfrom
matt/be-4219-jobs-ls-scope-where
Open

fix(jobs): scope jobs ls state-file rows to the resolved --where target (add --all)#582
mattmillerai wants to merge 2 commits into
mainfrom
matt/be-4219-jobs-ls-scope-where

Conversation

@mattmillerai

Copy link
Copy Markdown
Collaborator

ELI-5

comfy jobs ls has two sources: the ComfyUI/cloud server it queries, and the
job state files this CLI wrote to disk when you submitted something. --where
only ever routed the server half — the state-file half read every file
regardless of which target the job ran on. So comfy --json --where local jobs ls
happily listed cloud jobs from last week, forever (state files never expire).

Now the state-file rows are scoped to the resolved --where target too, and the
union view is one flag away: --all.

What changed

  • _gather_local_state_files(..., where=...) — new keyword filter. Rows
    whose state.where doesn't match are skipped; a missing/empty state.where
    reads as "local" (a defensive default — see the note below). where=None
    (the default) keeps the unfiltered union view, so every existing caller is
    behaviourally unchanged.
  • jobs ls resolves the target once (--where flag > COMFY_WHERE env —
    which is how the top-level comfy --where arrives > config default) and
    scopes state rows to it. New --all flag restores the union.
  • --orphaned stays unfiltered. Watcher cleanup is where-agnostic — you
    want to see a crashed cloud watcher from a default (local) shell. The
    where filter is also applied after the stale-watcher reap, so reaping
    keeps working the same in every view.
  • --watch threads the same scope, so the live table matches jobs ls.
  • server_rows are deliberately not filtered — they're already scoped by
    whichever backend was queried.
  • Payload gains scope ("local" / "cloud" / "all") so agents can see
    which view they got. Additive, envelope/1 unchanged; documented in
    comfy_cli/schemas/jobs.json and the shipped comfy skill doc.

Behaviour change worth flagging

This narrows the default jobs ls view. Anything relying on "one call shows
everything I ever submitted via this CLI" must now pass --all. That's the
intent — the pretty header already renders a single target's host, so a scoped
list is what the UI claimed to show — but it is a visible change and scope is
there so callers can detect it.

Downstream: comfy-local-mcp's get_queue always passes --where local and is
documented local-only, so it gets correct behaviour for free once this ships.

Verification

uv sync --frozen --extra dev, then:

  • pytest tests/2776 passed, 37 skipped.
  • ruff check . / ruff format --check . — unchanged from main (the 15
    UP038 findings and the one unformatted file, tests/comfy_cli/command/github/test_pr.py,
    are pre-existing on main; this diff adds none).

New tests (tests/comfy_cli/jobs/test_jobs.py): a seeded temp state dir with a
local, a cloud and a legacy row, covering default/--where cloud/--all/
--orphaned at both the gather level and through ls_cmd's emitted envelope,
plus --all in the machine-readable help contract.

End-to-end, against the real CLI with a seeded state dir (HOME pointed at
a temp tree holding one local + one cloud + one legacy state file):

$ comfy --json --where local jobs ls --port 65431
scope=local where=local rows=[('job-legacy','local'), ('job-local','local')]
$ comfy --json --where local jobs ls --port 65431 --all
scope=all   where=local rows=[('job-legacy','local'), ('job-cloud','cloud'), ('job-local','local')]
$ comfy --json --where local jobs ls --orphaned
scope=all   where=local rows=[]

No capability is removed — the union view was empirically re-attempted after
the change and is fully reachable via --all (second command above).

Judgment calls

  1. "Legacy files with no where" don't actually exist in a readable form.
    JobState.where has been a required, non-defaulted field since the state
    file was introduced, so jobs_state.read() already rejects a file that omits
    the key entirely (returns None) — it never reaches the filter. The
    reachable legacy shape is an explicitly null/empty value, which the
    state.where or "local" default handles. Both are pinned by tests. I did
    not give JobState.where a default: that's a shared-dataclass change
    well outside this ticket, and it would resurrect files today treated as
    truncated.
  2. scope is "all" for --orphaned, not the target. The ticket says
    scope is "all" or the target; since --orphaned gathers unfiltered, the
    truthful value is "all". scope is derived from the same variable passed
    to the gatherer, so it can't drift from the actual view.
  3. Normalised the emitted row where to "local" for a null/empty state
    where (JobRow.where is typed str and already defaults to "local"), so
    a row scoped as local can't report where: null. Nil blast radius —
    jobs_state.new() always writes a target.
  4. Replaced the three repeated _is_cloud(where) calls in ls_cmd with the
    single resolved target_where.
    Same semantics (and still monkeypatchable),
    but it guarantees the filter, the emitted where, and the pretty header can
    never disagree.
  5. Doc touch beyond the plan: the shipped comfy skill doc described
    jobs ls as "local state files + server queue", which is now incomplete for
    agents. One short paragraph + an example line were added.

Pre-existing issue observed, not fixed (out of scope)

While verifying the cloud path live: jobs ls --where cloud with credentials
present but the cloud API unreachable raises an uncaught URLError instead of
degrading to the state-file view its own comment promises (only typer.Exit is
caught). And when the preflight fails, its error envelope wins the
one-envelope-per-process guard, so the state-only fallback never reaches a JSON
caller. Both predate this change and are untouched here; the cloud scoping is
covered by unit tests instead.

…rget

`--where` only routed the SERVER query: `_gather_local_state_files` read
every state file in the jobs state dir regardless of each state's `where`,
so `comfy --json --where local jobs ls` kept surfacing cloud jobs from
previous runs (state files never expire). Agents wrapping the CLI with a
pinned `--where local` were getting cloud rows indefinitely.

- `_gather_local_state_files` takes a `where` filter; a missing/empty
  `state.where` reads as "local". `None` keeps the unfiltered union view.
- `jobs ls` resolves the target once and scopes state rows to it; the new
  `--all` flag restores the union. `--orphaned` stays unfiltered — watcher
  cleanup is where-agnostic.
- `--watch` threads the same scope so the live table matches `jobs ls`.
- Payload gains `scope` ("local" / "cloud" / "all") so callers can see
  which view they got. Additive; envelope/1 is unchanged.
- `server_rows` are deliberately not filtered: they are already scoped by
  whichever backend was queried.
@mattmillerai mattmillerai added agent-coded PR authored by the agent-work loop cursor-review Request Cursor bot review labels Jul 23, 2026
@mattmillerai
mattmillerai marked this pull request as ready for review July 23, 2026 23:11
@coderabbitai

coderabbitai Bot commented Jul 23, 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: 48 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: feb5c847-3c64-48d9-aca0-af3cce5d7cf4

📥 Commits

Reviewing files that changed from the base of the PR and between 85b62da and c7eabbf.

📒 Files selected for processing (4)
  • comfy_cli/command/jobs.py
  • comfy_cli/schemas/jobs.json
  • comfy_cli/skills/comfy/SKILL.md
  • tests/comfy_cli/jobs/test_jobs.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch matt/be-4219-jobs-ls-scope-where
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch matt/be-4219-jobs-ls-scope-where

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

@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. enhancement New feature or request labels Jul 23, 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.

Found 1 finding(s).

Severity Count
🟡 Medium 1

Panel: 6/8 reviewers contributed findings.

Reviewers that did not contribute: kimi-k2.5:adversarial (empty), kimi-k2.5:edge-case (empty)

Comment thread comfy_cli/command/jobs.py
The `--watch` branch returned before `if orphaned: local_only = True`, and
`_watch_ls` never received `orphaned`, so `jobs ls --watch --orphaned` still
queried the server, listed every state file instead of only crashed-watcher
rows, and scoped state files to the resolved target instead of the union —
diverging from the one-shot behavior this PR documents.

Hoist the `orphaned` handling and the `state_where` decision above the
`--watch` branch and thread `orphaned_only` through `_watch_ls` into
`_gather_local_state_files`, so the live table applies exactly the same
filters as `jobs ls`.

Tests cover the kwargs the watch path is driven with (default / --all /
--where cloud / --orphaned) plus `_watch_ls` actually forwarding them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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 enhancement New feature or request size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant