Skip to content

fix(workflow): emit an envelope for set-slot --stdout and vary under --json (BE-4215) - #579

Open
mattmillerai wants to merge 2 commits into
mainfrom
matt/be-4215-set-slot-stdout-envelope
Open

fix(workflow): emit an envelope for set-slot --stdout and vary under --json (BE-4215)#579
mattmillerai wants to merge 2 commits into
mainfrom
matt/be-4215-set-slot-stdout-envelope

Conversation

@mattmillerai

Copy link
Copy Markdown
Collaborator

ELI-5

comfy workflow set-slot --stdout is how a tool asks "apply this override and hand the
workflow back without touching my file." Under --json it handed back a bare workflow
object and never printed the envelope/1 wrapper that every machine caller looks for —
so the caller saw "no JSON" and the whole parameterize step failed. This makes that path
speak the envelope contract: the modified workflow now rides inside the envelope's data.
Piping by hand is unchanged.

What changed

comfy_cli/command/workflow.py

  • set-slot --stdout — the if stdout: branch wrote sys.stdout.write(serialized) and
    returned before renderer.emit(...), so no envelope was ever emitted in JSON mode.
    Now:
    • human mode (--no-json / TTY): unchanged — the raw workflow on stdout, pipeable.
    • JSON mode: one envelope/1 on stdout whose data carries
      workflow_json (the modified workflow), plus out: "stdout", wrote: null,
      the existing applied / warnings / stale block, and changed: false
      (nothing was written, so the envelope no longer claims a change).
  • vary without --out-dir — the sibling raw-stdout path the ticket asked me to audit
    had the same gap in a quieter form: it dumped N raw workflow lines onto stdout ahead of
    the envelope, and the envelope's data never contained the variants at all (written: [],
    no workflows). Same split applied: raw NDJSON in human mode, data.variants under --json.
  • --stdout / --out-dir / vary help strings now state where the result lands in each mode.

comfy_cli/schemas/workflow.json — documents out, workflow_json, variants, written,
out_dir, and widens wrote to ["string", "null"] (it can legitimately be null now).

Why the mode split is the right seam

docs/json-output.md and the renderer's own contract are explicit that in JSON mode stdout
carries only the envelope
; workflow get --where local in this same file already uses
exactly this if renderer.is_pretty(): write raw shape. The two stdout branches were
introduced in #470 — the commit that added the envelope contract — with no comment or test
asserting the bypass, so this reads as an oversight in that commit rather than a deliberate
carve-out.

Downstream verification (acceptance criterion 4)

Not asserted from my own tests — I ran the real comfy-cli binary and fed its stdout
through comfy-local-mcp's own _last_json_object + _unwrap_envelope, extracted verbatim
from that repo's origin/main (the exact code that raises the reported error):

comfy-cli build what set_workflow_slot(path, [...]) (default stdout=True) does
origin/main (before) raises ComfyCliError: comfy-cli returned no JSON (exit 0) — the reported bug, reproduced
this branch (after) returns data with keys applied, out, warnings, workflow, workflow_json, wrote; workflow_json contains the applied override (3.seed4215), wrote=None, out='stdout'

Also verified against the real binary: --no-json ... --stdout still prints the bare workflow;
--json workflow vary emits exactly one line (the envelope) with both variants in
data.variants; --no-json workflow vary still emits 2 NDJSON lines.

(The comfy-cli pin bump in comfy-local-mcp is explicitly out of scope per the ticket, so the
MCP won't pick this up until that pin moves. Its set_workflow_slot docstring — "write
modified to disk" — will want a one-line update to say modified["workflow_json"] when the
pin lands.)

Judgment calls

  1. Key name data.workflow_json (not making data be the workflow). Keeping the payload
    structured preserves applied, warnings, and the stale / object_info_stale signal that
    an agent needs; workflow_json matches how this file already names a workflow document on the
    cloud path. The cost is that a consumer reads data["workflow_json"] rather than data.
  2. Mode is decided by the renderer, so an un-flagged redirect changes shape.
    comfy workflow set-slot f.json a=b --stdout > out.json with no flag is non-TTY, and the
    renderer auto-selects JSON there — so that shell redirect now yields an envelope instead of a
    bare workflow. That is the documented machine contract (a non-TTY consumer is a machine), and
    --no-json is the escape hatch for a raw pipe; both are now spelled out in --stdout's help.
    Flagging it explicitly because it is the one behavior change beyond the bug itself.
  3. vary was in the ticket's "audit" list, and I fixed it rather than only noting it — the
    envelope it emitted was structurally valid but semantically empty (no variants anywhere in
    data), which is the same contract gap. Same 3-line shape, covered by its own two tests.
  4. changed: false on the --stdout path. --stdout writes nothing. The old
    changed=True was only ever reachable on the in-place path (the stdout branch returned
    early), so no existing behavior flips.

Not a capability-denying change — it restores a broken default; no deny/dead-end path,
"not supported" string, or dead-end test was added.

Testing

  • 5 new tests in tests/comfy_cli/command/test_workflow_slots.py: --json --stdout emits a
    single envelope/1 whose data round-trips the applied override (and leaves the file
    untouched); human-mode --stdout still prints the raw workflow; vary variants land in
    data.variants under --json; vary still emits NDJSON in human mode; variants is None
    when --out-dir was used. Red→green confirmed: 3 of them fail on the pre-fix source.
  • Full suite: 2773 passed, 37 skipped. ruff check + ruff format --diff (0.15.15, the CI pin) clean.

…--json (BE-4215)

`comfy workflow set-slot --stdout` wrote the modified workflow bare to stdout
and returned WITHOUT calling `renderer.emit(...)`, so under `--json` it produced
no `envelope/1` at all. Every machine caller — including the local Comfy MCP's
`set_workflow_slot`, whose default is `stdout=True` — failed the whole
parameterize step with "comfy-cli returned no JSON".

stdout belongs to the envelope in JSON mode (docs/json-output.md), so:

- set-slot --stdout: human mode still prints the raw workflow (pipeable);
  JSON mode emits an envelope whose `data.workflow_json` carries the modified
  workflow, with `wrote: null` / `out: "stdout"` and `changed: false`.
- vary without --out-dir: same split — raw NDJSON in human mode, variants in
  `data.variants` under --json (previously the variants were dumped onto
  stdout ahead of the envelope and never appeared in `data` at all).
- schemas/workflow.json documents the new keys and widens `wrote` to allow null.
@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 22:18
@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: 35 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: 39a8dabe-632c-455a-acd1-aa089b7098a8

📥 Commits

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

📒 Files selected for processing (3)
  • comfy_cli/command/workflow.py
  • comfy_cli/schemas/workflow.json
  • tests/comfy_cli/command/test_workflow_slots.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch matt/be-4215-set-slot-stdout-envelope
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch matt/be-4215-set-slot-stdout-envelope

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

@dosubot dosubot Bot added the size:M This PR changes 30-99 lines, ignoring generated files. label 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 5 finding(s).

Severity Count
🟠 High 1
🟡 Medium 1
🟢 Low 2
⚪ Nit 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/workflow.py
Comment thread comfy_cli/command/workflow.py
Comment thread comfy_cli/command/workflow.py
Comment thread tests/comfy_cli/command/test_workflow_slots.py Outdated
Comment thread comfy_cli/command/workflow.py Outdated
…s (BE-4215)

Cursor review follow-ups on the BE-4215 envelope split:

- vary (High): in human mode without --out-dir the NDJSON loop fell through to
  the pretty summary, so `comfy workflow vary ... --no-json > out.ndjson` ended
  with a non-JSON `✓ produced N variation(s)` line and broke strict
  line-delimited consumers. The summary/warnings now go to stderr whenever
  stdout carries the stream.
- set-slot/vary help + comment (Medium): a redirect makes stdout a non-TTY,
  which Renderer.resolve reads as JSON mode, so `--stdout > new.json` yields an
  envelope, not a raw workflow. Both now say --no-json is required for the raw
  form.
- set-slot (Nit): the workflow was serialized unconditionally but unused on the
  --stdout + JSON path; it is now serialized only where it is consumed.
- Both commands fold the object_info stale-cache note into `warnings` up front
  so the --stdout early return reports it (on stderr) instead of dropping it.
- The human-mode vary test asserted only on lines starting with `{`, which hid
  the trailing summary; it now requires every stdout line to parse as JSON and
  checks the summary lands on stderr.

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