Skip to content

feat(run): gate comfy run on paid partner nodes via --allow-spend (BE-4326)#591

Open
mattmillerai wants to merge 2 commits into
mainfrom
matt/be-4326-run-allow-spend
Open

feat(run): gate comfy run on paid partner nodes via --allow-spend (BE-4326)#591
mattmillerai wants to merge 2 commits into
mainfrom
matt/be-4326-run-allow-spend

Conversation

@mattmillerai

Copy link
Copy Markdown
Collaborator

ELI-5

comfy run could quietly spend your Comfy credits: if your workflow contained a
paid partner-API node (Seedream/Veo/Kling/Gemini/Flux-API/…) and the CLI found a
credential, it submitted and billed you with no warning. Its siblings comfy generate and comfy run-template already ask first. This adds the same
"are you sure?" gate to comfy run: it stops before spending, tells you which
nodes cost money, and only runs when you say yes (a prompt) or pass
--allow-spend. Ordinary free workflows behave exactly as before.

What changed

  • --allow-spend flag on comfy run (default False), help text mirroring
    run-template's, threaded through run.execute() and run.execute_cloud().
  • Shared _spend_gate interlock (copied from run-template's gate,
    BE-4113), inserted between partner-node detection and credential resolution:
    • Pretty + TTY: warns naming the nodes, typer.confirm(..., default=False);
      a decline → spend_consent_required + exit 1, nothing submitted.
    • JSON / non-TTY: fails closed with spend_consent_required, message tells
      the caller to re-run with --allow-spend, hint notes free workflows need no
      flag; partner_nodes in details.
    • Fires before _resolve_partner_credential() / cloud Client() so a
      refusal never triggers a network OAuth refresh.
  • Double-gate prevention: comfy run-template forwards allow_spend=True on
    its handoff to execute() (its own, strictly-stronger gate — it also inspects
    gallery signals — has already consented).
  • Docs: spend_consent_required row added to the comfy run table in
    docs/json-output.md; --allow-spend documented in the run section of
    comfy_cli/skills/comfy/SKILL.md.

Behavior contract

Case Behavior
No partner nodes Byte-identical — no prompt, no new output
Partner nodes + --allow-spend Runs as today
Partner nodes + no flag + TTY y/N prompt, default No
Partner nodes + no flag + machine mode exit 1, spend_consent_required, nothing submitted/spent
object_info fetch fails Fail-open ({} → no detection → no gate), same as run-template
Consented run, no credential partner_node_requires_credential still fires (unchanged)

Tests

tests/comfy_cli/command/test_run.py — new TestExecuteSpendGate (machine-mode
fail-closed, --allow-spend proceeds, no-partner never gates, TTY decline
blocks, TTY accept proceeds) and TestExecuteCloudSpendGate (cloud machine-mode
fail-closed + --allow-spend proceeds). The two existing partner-credential
tests now pass allow_spend=True (they exercise the post-consent credential
path). test_run_template.py — a handoff test asserting allow_spend=True is
forwarded so execute() doesn't re-gate. Full suite green (2775 passed, 37
skipped); ruff format + ruff check clean.

Judgment calls

  • Cloud path gated too (not just local). The ticket mandated the local path
    and left cloud conditional ("if cloud submit also bills partner nodes, gate it
    the same way"). Cloud does bill partner nodes server-side, so I applied the
    same gate in execute_cloud() for consistency, using the already-loaded cloud
    object_info with the same fail-open posture. This is a behavior change for
    existing non-interactive cloud runs of partner workflows (they now need
    --allow-spend), but it closes the same silent-spend hole and is covered by
    tests. Easy to drop if reviewers prefer local-only.
  • Simplest run-template handoff: always forward allow_spend=True (per the
    ticket's "simplest correct form"), since run-template's gate is strictly
    stronger and has already run.

Negative-claim falsification (per self-review discipline)

This diff adds a fail-closed / deny path, so per the falsification rule: the gate
does not deny a capability — running paid workflows remains fully reachable
via --allow-spend or an interactive "yes", proven by the passing
..._with_allow_spend_proceeds / ..._confirm_accepted_proceeds tests. It is a
consent checkpoint with a documented escape, not a #581-style dead-end.

Out of scope

comfy generate / run-template gate changes; comfy validate partner-node
surfacing (sibling ticket); MCP wrapper changes (follow-up, blocked on this).

…BE-4326)

`comfy run` silently spent Comfy credits when a workflow embedded partner-API
(paid) nodes and a credential resolved. `comfy generate` and `comfy run-template`
both gate on spend consent; `comfy run` was the hole.

Add an `--allow-spend` flag (default False) and a shared `_spend_gate` consent
interlock mirroring run-template's gate (BE-4113):
- Pretty + TTY: warn naming the nodes, confirm (default No); decline →
  `spend_consent_required` + exit 1.
- JSON / non-TTY: fail closed with `spend_consent_required`, telling the caller
  to re-run with `--allow-spend`; nothing submitted, nothing spent.
The gate fires BEFORE partner-credential resolution / cloud auth so a refusal
never triggers a network OAuth refresh. Detection stays fail-open (empty
object_info → no gate). Applied to both the local and cloud submit paths (cloud
also bills partner nodes server-side).

run-template forwards `allow_spend=True` on its handoff to execute() so its
already-consented run is not gated a second time. Docs + skill updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 24, 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: 59 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: 415b7e17-739a-48af-8856-12ee2f49737d

📥 Commits

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

📒 Files selected for processing (7)
  • comfy_cli/cmdline.py
  • comfy_cli/command/run/__init__.py
  • comfy_cli/command/templates.py
  • comfy_cli/skills/comfy/SKILL.md
  • docs/json-output.md
  • tests/comfy_cli/command/test_run.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-4326-run-allow-spend
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch matt/be-4326-run-allow-spend

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 24, 2026
@mattmillerai
mattmillerai marked this pull request as ready for review July 24, 2026 06:39
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. enhancement New feature or request labels Jul 24, 2026
@mattmillerai mattmillerai added the cursor-review Request Cursor bot review label Jul 24, 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 6 finding(s).

Severity Count
🟠 High 1
🟡 Medium 2
🟢 Low 3

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/run/__init__.py
Comment thread comfy_cli/command/run/__init__.py
Comment thread comfy_cli/command/templates.py
Comment thread comfy_cli/command/run/__init__.py Outdated
Comment thread comfy_cli/command/run/__init__.py Outdated
Comment thread comfy_cli/command/run/__init__.py
Address cursor-review panel findings on PR #591:

- Guard sys.stdin.isatty(): detached/pythonw contexts can leave
  sys.stdin as None (AttributeError) or closed (ValueError); route
  those to the fail-closed machine-mode error instead of crashing.
- Escape partner class_type names before Rich-markup interpolation so
  a name like `[bold]` can't raise MarkupError or inject formatting.
- Align docs/json-output.md: the cloud spend_consent_required variant
  carries `where: "cloud"` in details, not host/port.

The fail-open catalog-unavailable findings (High/Medium) are a
deliberate, load-bearing design choice; deferred to a follow-up for a
product decision rather than reversed here.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. labels Jul 24, 2026
@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-4370 — Decide fail-open vs fail-closed for the comfy run spend gate when the node catalog is unavailable (BE-4326 follow-up)

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