Skip to main content

日本語 | 中文 | Español | Français | हिन्दी | Italiano | Português (BR)

comfy-preflight

CI PyPI npm MIT License Landing page

A gate that runs on a ComfyUI workflow graph in the seconds before it is submitted,
and halts a submission that would spend credits producing a known-wrong result.
It does not submit. It does not fix your graph.


The gap it lives in

A Comfy Cloud dry_run PASS does not prove link sanity. A hand-retyped payload with a self-referencing node link — VAEDecode.samples = ["14", 0], the node pointing at itself — returned status: validated.

The provider's validator answers is this graph well-formed enough to run. It does not answer is this the graph you meant. Every check here lives in that gap, and each one was paid for by a run that got past dry_run.

Submission is the irreversible act with no real undo, and a preflight is a compensator you run BEFORE instead of after. A completed cloud job is billed; the only compensator afterwards is cancel it if it is still queued, otherwise none. That is the whole argument for this package.

Install

npx @mcptoolshop/comfy-preflight check graph.json   # no Python needed
pip install comfy-preflight                          # for the in-process gate
pip install "comfy-preflight[mcp]"                   # + the MCP stdio server

The npx launcher downloads a binary from this repo's GitHub Release and verifies its SHA256 against the checksums in that same release before running it. Python ≥ 3.11; binaries for linux-x64 and win-x64 (macOS installs via pip).

Use it

The production gate — in-process, on the submit path:

from comfy_preflight import preflight

# Inside the function that submits. Not in a shell step before it.
preflight(graph, register, input_dims=(width, height))   # raises PreflightHalt
submit(graph)                                            # only reached if nothing raised

The development door:

comfy-preflight check graph.json \
  --input-dims 1072x1024 --register subject.json --saved sidecar.json --json
exit meaning
0 nothing halted — PASS, ADVISORY or NOT_APPLICABLE, each named in the output
1 HALT
2 nothing was examined — bad arguments, unreadable file, or an internal error

ADVISORY exits 0 on purpose: a nonzero status stops a && chain, which would turn an advisory into a halt in every shell that runs one.

What it checks

# check halts on
1 Link topology a node input reading its own node; a link to a node id not in the graph
2 The inverted register scan a declared register that does not match the graph's construction — in both directions
4 Saved-is-submitted the saved sidecar and the submitted payload differing as parsed graphs
5 Generator-legal frame a dimension the effective frame's VAE cannot decode at
8 Declared envelope nothing — it never halts. Out-of-band is an ADVISORY

Three checks from the original design are not built, and are named rather than quietly missing: recipe-vs-profile agreement (no subject-profile fixture exists), estimate-before-submit (transport-side — no graph-structural operand), and anchor reproduction (needs the graph builder; the corpus holds outputs, not the scripts that made them).

Check 2 — the one that names the method

When a subject's register declares no style adapter, the claim being asserted is not "the weight is 0.0". It is that no loader node and no adapter card reference exist anywhere in the graph.

A weight of 0.0 is not a weight of zero on a loaded card; it is no card.

And it asserts the mirror image. A decided positive weight with no loader node is silently inert: the run completes, costs money, and produces base-model output while every log line says the adapter was requested. That direction produces no signal a human could notice — which is exactly where a gate earns its place over a person looking.

Check 5 — the effective frame, not the declared one

1066 / 8 = 133.25 encodes to 133 latent columns and decodes to 1064, putting every output 2 px off its control image and breaking every downstream pairing.

The defect happened upstream of the graph. The 1066 was derived correctly from a mesh, the image was rendered at that width and uploaded, and the graph never declared it — so a check reading graph literals could not have caught the incident that motivates it. The operand is the frame the run will actually produce, which on an img2img graph is the input image's dimensions.

÷8 halts. ÷16 advises. A floor and a preference, not two floors.

Check 8 — advisory, and honest about what it cannot say

For each checkpoint the graph loads, parameters are compared against a cited envelope table. Every entry carries its band, source URL, retrieval date and a quote of the card's own words — and the constructor refuses to build an entry that does not.

The day-one entry is Qwen-Image-InstantX-ControlNet-Union. It carries the controlnet_conditioning_scale band its card documents, and a declared absence for denoise, because that card publishes no denoise range at all — verified against the live card rather than recalled. So a run on a graph at denoise=0.92 reports the 0.92, names the parameter, and says plainly that it cannot judge it and why.

Reporting the value it cannot judge is the honest half of the finding. Inventing a band to judge it against would be the dishonest half.

The adoption contract

Call it in-process on the submit path. There is no skip flag.

The check lives inside the tool that performs the irreversible step. A preflight in a shell chain before a submit is a transport, not a guard — in the incident that produced this rule, 47,020 texels were committed after a gate had already fired, because a PowerShell chain walked past a failing exit code. Nobody decided to proceed; the construction was incapable of stopping.

The CLI and the MCP server are both transports, not gates. Reading a HALT from either and then submitting from somewhere else is that same chain with a nicer interface.

No function here takes a skip, force, warn_only or enabled parameter, and tests read each signature rather than trusting the docs. Every gate raises; none is a bare assertpython -O deletes assert silently, and CI runs the whole suite under -O and PYTHONOPTIMIZE=1 to prove the gates survive it.

Over MCP

python -m comfy_preflight.mcp_server     # stdio
npx @mcptoolshop/comfy-preflight mcp     # same server, no Python required

One tool, preflight, returning the same structured result the library returns — a test asserts byte-identity. A HALT is a successful call returning {"verdict": "halt", ...}, because reporting it as a protocol error would throw away the structure the caller needs.

Security

No network calls, no credentials, no telemetry, and it writes nothing anywhere. It reads a graph and a profile, and returns a verdict. Full threat model — data touched, data not touched, and the permissions required — in SECURITY.md.

Documentation

📖 The handbook — getting started, every check in detail, the adoption contract, and the envelope table.

License

MIT — see LICENSE.

Built by MCP Tool Shop

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

comfy_preflight-1.0.0.tar.gz (93.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

comfy_preflight-1.0.0-py3-none-any.whl (59.6 kB view details)

Uploaded Python 3

File details

Details for the file comfy_preflight-1.0.0.tar.gz.

File metadata

  • Download URL: comfy_preflight-1.0.0.tar.gz
  • Upload date:
  • Size: 93.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for comfy_preflight-1.0.0.tar.gz
Algorithm Hash digest
SHA256 a5ebdf399dcba09e246404e517381007d4560988556e8552da7e5e60924dd1f3
MD5 f0a284708c5fe8a6ee698abcaf1d7554
BLAKE2b-256 a6601694aab9860ea57886e6a777c356826c74f83af8ac9a8e90d8761f73a47c

See more details on using hashes here.

Provenance

The following attestation bundles were made for comfy_preflight-1.0.0.tar.gz:

Publisher: release.yml on mcp-tool-shop-org/comfy-preflight

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file comfy_preflight-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: comfy_preflight-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 59.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for comfy_preflight-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b93a58261fbb05989e2e5dc12c34c65ce9375a914769f11a20795c08f4156b54
MD5 056650843fa935aa9b916a0b9ac4d9c7
BLAKE2b-256 a33e50e4593c1acc0b26233eeb79d25d55dcd371fe8e0c3053e7d8ff0fecde45

See more details on using hashes here.

Provenance

The following attestation bundles were made for comfy_preflight-1.0.0-py3-none-any.whl:

Publisher: release.yml on mcp-tool-shop-org/comfy-preflight

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page