日本語 | 中文 | Español | Français | हिन्दी | Italiano | Português (BR)
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_runPASS does not prove link sanity. A hand-retyped payload with a self-referencing node link —VAEDecode.samples = ["14", 0], the node pointing at itself — returnedstatus: 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 assert —
python -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
Metadata
Release files for comfy-preflight 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| comfy_preflight-1.0.0.tar.gz | 93.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| comfy_preflight-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 152.7 kB
Release files / comfy_preflight-1.0.0.tar.gz
| Download URL | comfy_preflight-1.0.0.tar.gz |
|---|---|
| Size | 93.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a5ebdf399dcba09e246404e517381007d4560988556e8552da7e5e60924dd1f3
|
|
BLAKE2b-256 checksum How to use checksums |
a6601694aab9860ea57886e6a777c356826c74f83af8ac9a8e90d8761f73a47c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 14, 2026.
Transparency logRelease files / comfy_preflight-1.0.0-py3-none-any.whl
| Download URL | comfy_preflight-1.0.0-py3-none-any.whl |
|---|---|
| Size | 59.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b93a58261fbb05989e2e5dc12c34c65ce9375a914769f11a20795c08f4156b54
|
|
BLAKE2b-256 checksum How to use checksums |
a33e50e4593c1acc0b26233eeb79d25d55dcd371fe8e0c3053e7d8ff0fecde45
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 14, 2026.
Transparency log