Skip to content

feat: comfy run-template — fetch, fill params, spend-gate, run to completion (BE-4131) - #578

Merged
mattmillerai merged 5 commits into
mainfrom
matt/be-4131-run-template
Jul 23, 2026
Merged

feat: comfy run-template — fetch, fill params, spend-gate, run to completion (BE-4131)#578
mattmillerai merged 5 commits into
mainfrom
matt/be-4131-run-template

Conversation

@mattmillerai

Copy link
Copy Markdown
Collaborator

ELI-5

comfy templates could only browse the gallery — you could list templates and fetch their JSON, but actually running one meant a manual fetch → edit → comfy run chain. This PR adds comfy run-template <name>: one command that fetches a gallery template, fills in any inputs you pass with --param KEY=VALUE, and runs it to completion on your local ComfyUI. If the template uses paid partner-API nodes (Flux Pro, Kling, Gemini, …), the command stops and asks for consent first — --allow-spend (or an interactive yes) is required before anything that burns Comfy credits is submitted.

What

  • New top-level verb comfy run-template <name> (comfy_cli/command/templates.py, registered in cmdline.py):
    • Resolves <name> against the gallery index (same close-matches affordance as templates fetch), pulls templates/<name>.json.
    • --param KEY=VALUE (repeatable) fills parameterized inputs via the existing CQL slot engine (get_template_schema / apply_slots). KEY is a slot address (6.text, 62/34.text) or a unique slot name (prompt); VALUE is JSON-parsed with string fallback — identical semantics to comfy workflow set-slot. Unknown/ambiguous keys error with the candidate list so agents can self-correct.
    • Spend gate: before anything is submitted, the workflow's node types (including nodes inside UUID subgraph definitions) are checked against object_info (api_node: true / partner/ category — same signals as comfy run's partner detection), belt-and-suspendered with the gallery index's own paid markers (API tag, provider logos). Paid template + no --allow-spend → interactive confirm on a tty, hard error spend_consent_required (new registered error code) otherwise. Nothing is submitted, no credits spent.
    • Hands off to the existing run path (command.run.execute): UI→API conversion, partner-credential injection, preflight validation, execution, jobs state — nothing re-implemented. Runs to completion by default (wait=True); --async submits and returns. --host/--port/--timeout/--verbose/--api-key/--json/--gallery/--refresh mirror the surrounding commands.
  • 14 new offline tests (tests/comfy_cli/command/test_run_template.py): resolution failures, param syntax/unknown-key/API-format rejection, the gate in all four postures (gallery-signal block, partner-node block without gallery signals, --allow-spend unblock, interactive decline), OSS pass-through, param filling by address and by name, wait/async handoff, temp-file cleanup.

Judgment calls

  • BE-4103's shared spend-gate does not exist yet (no branch, PR, or commit in this repo references it), so there was nothing to "route through." I implemented the gate inline in run-template with a registered error code and the --allow-spend consent flag — the same consent-before-credit contract the BE-4103 ticket describes for comfy generate. When BE-4103 lands a shared helper, this call site is a one-line adoption.
  • Gallery-signal calibration was checked against the live index (528 templates): 0 of 224 OSS templates carry provider logos, so the provider signal cannot over-gate OSS runs; openSource: false alone is deliberately NOT a signal (60 local-model workflows like sdxl_simple_example carry it). Exactly one OSS template (utility-gan_upscaler) carries the API tag and would be conservatively gated — cost is one flag, spends nothing.
  • Missing-model behavior is deferred to the existing run path: local model gaps surface through comfy run's preflight//prompt validation errors, which the local MCP already maps (local-mcp-nomodel-001); the per-template runnable/missing verdict is feat: comfy templates check — per-template runnable/missing/api-required verdict #557's scope (comfy templates check, in review). The command help points OSS users at comfy model download.
  • The completion envelope comes from the run path and is labeled command: "run" — kept as-is rather than threading a label parameter through execute()'s stable contract.
  • Residual under-gate window: an API-format paid template + unreachable object_info would skip node detection — but every current gallery template is frontend-format (verified), frontend conversion hard-fails without object_info, and credential injection would also be absent, so no spend can occur silently.

Testing

@mattmillerai mattmillerai added the agent-coded PR authored by the agent-work loop label Jul 23, 2026
@mattmillerai
mattmillerai marked this pull request as ready for review July 23, 2026 00:59
@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: 5 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: 0142e41d-7e44-4692-ad56-3a18bd82fe6f

📥 Commits

Reviewing files that changed from the base of the PR and between f132a08 and bc2b874.

📒 Files selected for processing (4)
  • comfy_cli/cmdline.py
  • comfy_cli/command/templates.py
  • comfy_cli/error_codes.py
  • tests/comfy_cli/command/test_run_template.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch matt/be-4131-run-template
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch matt/be-4131-run-template

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

@mattmillerai mattmillerai added the cursor-review Request Cursor bot review label Jul 23, 2026
@dosubot dosubot Bot added size:L This PR changes 100-499 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.

⚠️ Review failed

Judge call failed (status=parse_error): Could not parse JSON findings from output. First 500 chars:
I've verified the key claims against the actual code. Critical findings from my investigation:

- The top-rated panel finding (`providers` vs `logos` mismatch, rated high/critical by Gemini) is a **false positive**: `_flatten_templates` (outside the diff, line 106) remaps `logos[].provider` → a flat `providers` list on every row before it reaches `_gallery_paid_signals`. The reviewers relied on the raw fixture shape without tracing the transform. Dropped.
- The integer-slot-address findings are 

Re-trigger by removing and re-adding the cursor-review label.

@mattmillerai mattmillerai added cursor-review Request Cursor bot review and removed cursor-review Request Cursor bot review 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.

⚠️ Review failed

Judge call failed (status=parse_error): Could not parse JSON findings from output. First 500 chars:
I've verified the findings against the actual code. Key results from my investigation:

- **Confirmed real:** The host parsing (`host.split(":")` + `int(s[1])`) reimplements the shared `comfy_cli/host_port.py` parser, whose docstring explicitly says "callers should not re-implement it." It crashes on bad ports, mangles IPv6, and skips the `frozenset("/@​?#")` validation that `execute` applies — but only *after* the pre-execution probe/object_info fetch. This unifies the host-crash, IPv6, and SSRF

Re-trigger by removing and re-adding the cursor-review label.

@bigcat88 bigcat88 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I put this through a full live cycle against a real local ComfyUI and the feature itself is in great shape — the two blockers are coordination with your own in-flight PRs, both minutes to fix.

What I verified live (server on :8189, shared model library mounted):

  • Full happy path: comfy --json run-template sdxl_simple_example --port 8189 with 7 --param fills (both ckpt_names by address to a Lightning checkpoint, steps, prompt) → gate correctly silent for OSS → UI→API conversion → ran to completion → ok: true with the output PNG on disk. The one-command promise is real.
  • Spend gate on api_seedance2_0_r2v without --allow-spend → exit 1, spend_consent_required, with both evidence channels populated: partner_nodes: ["ByteDance2ReferenceNode"] and gallery_signals: ["tag:API", "provider:ByteDance"] — so the providers field really is in the live index and the belt-and-suspenders design works.
  • Unknown --param key → candidate address list; missing-model OSS run → the run path's per-node enum validation with suggestions. Both are exactly the self-correction surface an agent needs.
  • Full unit suite on this branch merged with current main: 2557 passed.

The two blockers are inline: the spend_consent_required collision with #577 (empirically: the two branches auto-merge cleanly and the combined tree fails test_no_duplicate_codes — whichever lands second breaks main), and the hand-rolled host/port block that diverges from comfy run's actual resolver and will leave run-template deaf to #571's COMFY_LOCAL_URL — on the very MCP boxes this command targets (BE-3956 × BE-4131).

One non-blocking note: the PR body's "BE-4103's shared spend-gate does not exist yet" is now stale — #577 exists and is approved. When you adopt its helper (or keep the inline gate), consider also aligning the consent flags (--allow-spend here vs --yes + spend.auto_confirm there); not honoring the persisted consent here is conservative and fine, but the naming split is UX debt worth a follow-up ticket.

Comment thread comfy_cli/error_codes.py Outdated
Comment thread comfy_cli/command/templates.py Outdated
…nt_required doc (BE-4131)

Address review (bigcat88):

- templates.py: replace the hand-rolled host/port block with the shared
  `parse_host_port_arg` + `resolve_host_port` (host_port.py), exactly like
  `comfy run`'s local branch (cmdline.py). This validates the host (rejecting
  URL-injection characters), brackets IPv6 literals, and honors
  config.background — none of which the old inline block did — and keeps
  run-template aligned with #571's COMFY_LOCAL_URL routing.

- error_codes.py: broaden the `spend_consent_required` description from
  run-template-specific to cover both surfaces (`comfy run-template` and
  `comfy generate`, the code #577 also registers) so the shared entry is
  ready for the two PRs to reconcile onto one registration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mattmillerai
mattmillerai requested a review from bigcat88 July 23, 2026 17:35
@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:

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

  • BE-4193 — Align spend-consent UX flags across comfy run-template and comfy generate

@mattmillerai

Copy link
Copy Markdown
Collaborator Author

@bigcat88 both blockers from your live cycle are addressed in b0dd039 — re-requesting review:

  1. Host/port — the hand-rolled block is gone; run-template now goes through parse_host_port_arg + resolve_host_port, byte-for-byte the same path as comfy run's local branch (cmdline.py:877-884). That gets host validation, IPv6 bracketing, and — the thing you flagged — it inherits feat: honor COMFY_LOCAL_URL env var for the local ComfyUI address (BE-3959) #571's COMFY_LOCAL_URL support the moment feat: honor COMFY_LOCAL_URL env var for the local ComfyUI address (BE-3959) #571 lands, on exactly the MCP boxes this targets. Thread resolved.

  2. spend_consent_required collision with feat(generate): spend-gate — consent before credit spend (BE-4103) #577 — the entry's description is broadened to cover both run-template and generate so the canonical text is ready. The full dedup can't land on feat: comfy run-template — fetch, fill params, spend-gate, run to completion (BE-4131) #578 alone: test_no_duplicate_codes is bidirectional, so feat: comfy run-template — fetch, fill params, spend-gate, run to completion (BE-4131) #578 can't drop its entry while its source still emits the code — that'd break feat: comfy run-template — fetch, fill params, spend-gate, run to completion (BE-4131) #578's own CI before feat(generate): spend-gate — consent before credit spend (BE-4103) #577 is on main. Plan stands: once one of the two merges, the second rebases onto main and drops the duplicate entry (keeping the emit site). Left that thread open as the coordination tracker.

Non-blocking consent-flag naming note (--allow-spend vs #577's --yes/spend.auto_confirm) recorded as a follow-up for after both land. Full unit suite + registry/templates tests green locally, CI green.

…131)

#577 (the `comfy generate` spend-gate) landed the canonical
`spend_consent_required` entry on main. Merging main into this branch surfaced
the duplicate registration this branch added — `test_no_duplicate_codes` would
break main on whichever PR merged second, with no textual conflict to warn
anyone.

Reconcile per review: drop this branch's duplicate entry and broaden the
surviving (main / #577) entry's description to cover both surfaces
(`comfy run-template` + `comfy generate`), reusing #577's entry as bigcat88
asked. Registry (incl. test_no_duplicate_codes), run-template, and spend-gate
tests green; full suite 2770 passed / 36 skipped.
@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Jul 23, 2026
@mattmillerai
mattmillerai merged commit 85b62da into main Jul 23, 2026
17 checks passed
@mattmillerai
mattmillerai deleted the matt/be-4131-run-template branch July 23, 2026 21:18
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 23, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

agent-coded PR authored by the agent-work loop cursor-review Request Cursor bot review enhancement New feature or request lgtm This PR has been approved by a maintainer size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants