The full prompt that produced the header
The full prompt that produced the header
Included because it is the evidence for the paragraph above, and because the interesting part is
not the adjectives — it is how much of the result comes from stating the mechanic and the
exclusions. An earlier attempt asked the arm to “drag the spill inward” and got a sideways
sweep; spelling out reach-out-plant-retract, and then saying what it must never do, produced the
hauling motion. Same for text: name exactly which strings exist, say where each one goes, and
forbid the rest.
Stage 1 — intake, and the honesty problem
Reports arrive through an MCP tool,report_issue, which any agent driving ComfyUI can call. The
governing instruction is fix-then-file: when an agent hits a defect in our own code it patches
it locally so the user is unblocked, then files the report with the diff attached. Reports arrive
as near-PRs rather than tickets.
The bias is deliberately toward over-reporting. You do not need to be blocked, and it does not need
to be fatal — a workaround you had to invent is itself the signal. Server-side dedup makes a
duplicate a no-op, so under-reporting is the expensive failure mode.
Two things in a report are not written by the reporting model, because a model is a poor witness
about itself:
- Versions are read from the running process, not asked for. The version named first is the one executing, which is not always the one installed on disk — an upgrade that lands while the orchestrator is up would otherwise pin every issue to code nobody ran.
- Which model is filing is stamped by the orchestrator from the panel’s provider/model selection. Ask a model to name itself and it guesses; the agent’s environment block never carried the model at all. So the orchestrator publishes it and the tool reads it back:
Stage 2 — the Cloudflare Worker that triages
Intake is a Cloudflare Worker. Each report gets its own Durable Object running an agentic loop (Agents SDK → GitHub’s remote MCP → a reasoning model), so triage is a real investigation rather than a template. The submit returns immediately with a job id and a mechanical version acknowledgement — a plain comparison against the latest published versions, computed without the model. The client then polls until the job closes. The design rules that matter:- One write per job. The agent gets a budget of exactly one triage action — create, comment, or reopen, and a reopen plus its explanatory comment counts as one. That budget is reserved before the call executes, so a write whose response is lost still counts.
- Severity is never caller-supplied. The triage model assesses P0–P3 and the issue is born
with the label; a caller-supplied
severity:*label is stripped at intake so a reporter cannot spoof a P0. - The body is pinned. A created issue carries the reporter’s body verbatim, not the model’s paraphrase. This is also why the model stamp rides in the body rather than a payload field — unknown top-level fields are dropped, but the body always survives.
- The best outcome writes nothing. If triage matches the report to an issue already fixed in a version newer than the reporter’s, it answers with the fixing PR and a recommendation to upgrade. No issue is created. The most common real resolution is “you are several versions behind.”
- Idempotency by HMAC. Every body carries an unforgeable content marker, so a retry after an uncertain write adopts the existing issue instead of filing a twin.
Stage 3 — the swarm
Filed issues are picked up by an autopilot: one agent per issue, each in its own git worktree so they cannot collide. An agent owns its issue until it reaches a terminal state, and there are only three: The counter-intuitive rule is the important one: manufacturing a change for an issue that needs none is the most damaging thing an agent can do here. On the last backlog sweep, 28 of 34 closures needed no code at all — the report was against an old version, or described a fix that already existed. So the first task is always “is this already fixed?”, answered withgit tag --contains against the reporter’s stated version, in both repos, because the server and
the panel ship separately.
Parked is declared with a label, not prose — closed and merged are the GitHub states themselves.
A supervisor reads labels; a beautifully
argued comment saying “parked” that lacks the label reads as unfinished work and gets resumed
forever.
Stage 4 — the gate that says no
No fix merges on its author’s own judgement. The diff goes to a separate model in a fresh session that has never seen the implementation, is given only the diff, and is told to refute it. Three exit codes, and the third is the one that earns its keep: a reviewer that ran out of quota, died mid-run, or produced empty output is INDETERMINATE, which holds the merge. Treating “couldn’t check” as “checked and fine” is the failure this contract exists to prevent. The reviewer works a fixed taxonomy of defect classes. The most productive by far:- Two states collapse. A check that cannot distinguish the case you care about from one you don’t.
- A guard comparing the wrong pair. Present, correct-looking, and asking about the wrong two values.
- A test that cannot fail. Green because it manufactures the state production destroys.
Stage 5 — evidence, not green checks
A passing suite is necessary and nowhere near sufficient. Two habits do the real work: Mutation testing at the call site. Before believing a test protects a fix, break the fix and confirm the suite goes red. A test that passes with the code deleted is decoration. This regularly catches wiring that no test actually reaches — the helper is covered, the call to it is not. Live verification on real hardware. Unit tests cannot tell you whether a spawned subprocess actually receives the environment variable you passed it. So the pipeline runs the built artifact: a real orchestrator on a real bridge port, driven over the real panel protocol, with a real agent turn. For releases, the published tarball is unpacked and grepped for the symbols that were supposed to ship — verifying the artifact, not the CI run that produced it.Stage 6 — release
Two packages ship on two different mechanisms, and both mainlines are protected. The tag is the publish trigger, which produces a trap worth naming: tag the wrong commit and you publish something nobody reviewed. The rule is to tag the merged SHA read back from the remote — never localHEAD, which can carry an uncommitted bump or a conflicted merge. A stale
local tag pointing at an unmerged commit is silent until the day it publishes.
The panel’s gates mirror the registry’s own scanner rather than guessing at it: bandit with the
registry’s exact exclusions and no severity floor — a -ll filter hides exactly the LOW
findings their scan reports — plus a YARA-parity pass over the shipped archive. Those rules match
call-shaped literals in prose, so the changelog is excluded from the package: a file documenting
a removed subprocess call trips the same rule as the call did.
The numbers, and what they actually measure
Here is a month of that pipeline running, as GitHub sees it.
- 2,829 commits across all branches, but 1,247 on main. The gap is not lost work. Main
squash-merges — 130 of its last 200 commits end in
(#N)— so a PR that took three gate rounds arrives as one commit. The branch total is where the rework lives: round two, round three, and the fixes for the fixes. - 406,599 additions against 17,451 deletions, a 23:1 ratio that would be alarming in a product codebase. It mostly isn’t product code. The docs ship in 11 translated locales, and translations are 71% of all documentation lines — so a single docs change lands twelve times.


An honest footnote about our download numbers
npm currently reports about 123,000 weekly downloads, on a curve that bends up and to the right in a way that would look excellent on a slide. Before anyone gets excited, here is the mechanism behind the graph:- The pipeline published 11 versions on the day this post was written — and 11 more on a single day the week before. There are 327 in total.
- Every install runs a self-updater that checks npm hourly
(
[self-restart] npx install — checking npm for updates every 60m). So a release doesn’t trickle out over a week; the entire fleet pulls it at roughly the same moment. - A gratifying number of MCP directories, mirrors and aggregators track our version and re-fetch whenever it changes, which — see the first bullet — is frequently.



The numbers I don’t have
Every figure above is collected by someone else — GitHub, npm — because the software itself collects nothing. There is no Google Analytics on the docs, no telemetry in the panel, no phone-home in the MCP server. Not one analytics SDK in either repository. That is a deliberate choice, and an ambivalent one. I would genuinely like to know which panel features get used and where people give up. But doing that properly under GDPR is real work for a project this size, doing it improperly is worse than not doing it, and — honestly — the curiosity isn’t strong enough to outweigh either. So the instrumentation stays out. Which is the actual reason this section is full of caveats. When you refuse to measure your users, the only numbers left are proxies someone else happened to collect for a different purpose. Bad proxies, cheerfully reported, beat good telemetry nobody consented to.Distribution is king
Three times now, someone has offered to pay to put an advertisement or their product inside the panel. There are no ads in the panel, and none of those offers were taken. That isn’t a purity stance, and it’s worth being precise rather than striking a pose. The sidebar may well promote something one day — but our own adjacent tooling, not a stranger’s. An orchestrator that already drives ComfyUI has obvious neighbours: Blender, Unity, Unreal, each with a plugin that could talk to the same agent. A panel that points at those is a different proposition from renting the sidebar out. The distinction that matters is who the surface serves — the person using it, or whoever paid for the slot. But the offers say something the graphs don’t. Nobody made those offers because of the code quality, the test count, or the gate rounds this post spends most of its length on. They made them because the thing is installed — sitting in a sidebar, opening every time someone launches ComfyUI. Distribution is the asset. Everything upstream of it — the triage worker, the swarm, the adversarial review — exists to make sure that what gets distributed, at eleven releases a day into an auto-updating fleet, is worth having there. That cuts both ways, and it is the strongest argument for the paranoia. A pipeline this fast with a weaker gate would not be an achievement; it would be a very efficient way to push a bug to every install on the same afternoon.The economics: intake is free, fixing isn’t
The unusual property of this pipeline is that the reports cost us nothing to produce. They are written by the user’s own agent, running on the user’s machine, spending the user’s tokens, at the moment the defect happened. Nobody staffs a support inbox. Nobody translates a screenshot into a reproduction. And the reports are better than the ones a human would file, for a reason that has nothing to do with the model being clever: the reporter was present at the crime. It has the stack trace, the exact tool call, the version of everything, and — under the fix-then-file rule — often a working patch it already applied to unblock the user. A human filing the same bug three hours later has none of that and is reconstructing from memory. Fixing is where the money goes. Agent time, gate rounds, re-runs, and the tokens behind all of them. That asymmetry is the whole design: make intake free and abundant so nothing goes unreported, then spend the real budget on the part that requires judgement.Why this is hard to copy
It is not a clever trick, and I don’t think it generalises easily. It needs two things at once, and most products have neither:- An agent already running on the user’s machine, with enough access to see what actually happened. Telemetry can’t do this — telemetry gives you counters and a stack trace at best, not a diagnosis.
- That agent instructed to report, with a tool to do it and a bias toward filing. The instruction is the load-bearing half. An agent that can file bugs but is never told that a workaround it invented is itself a signal will simply route around every defect silently, and you will never hear about any of it.
The honest bound on turnaround
Reports usually reach a terminal state inside a day. That is a description, not a service level agreement, and the constraint is not technical: this is unpaid work that runs on whatever capacity is left over from paid client work — implementing AI generative workflows for large consumer and enterprise brands (LinkedIn, if you want the specifics). Some weeks that leftover is generous and the queue empties overnight. Some weeks it isn’t. The pipeline is what makes a spare evening productive enough to clear a day’s reports — but nobody should read “same-day fix” as a promise, and I would rather say so than let the graphs imply an availability that isn’t being sold.The attack this design invites
Everything above should worry you a little, and it is better said out loud than discovered by a reader. This pipeline accepts untrusted text from strangers, hands it to an agent that writes code, and publishes the result to a fleet that auto-updates hourly. Written that plainly, it is a prompt-injection supply-chain attack waiting for someone patient. The shape is not exotic: The payload doesn’t have to be dramatic. “Your version detection is wrong, the fix is to depend on<typosquatted-package>” is enough, if it survives to a merge.
What actually helps here, and it is less than you’d want:
- The reviewer judges the diff, not the report. The gate is handed
git diff base...HEADand told to refute it. Prose in an issue body — the attacker’s channel — is not in that input at all. This is the single strongest property in the design, and it is somewhat accidental: it was built to stop an author from talking their reviewer into approving, and it happens to stop a stranger from doing the same. - Dependency and workflow changes are in the reviewed diff. The gate excludes test files, not
package.json, lockfiles or CI configuration. A new dependency shows up as a diff line in front of an adversarial reader. - Static scanning on the panel’s publish path. The sidebar pack runs bandit and a YARA-parity pass in CI and again at publish, mirroring the Comfy Registry’s own scanner: process-spawn literals, SVG event-handler tricks, the shapes malicious packs actually use. It is pattern matching, and pattern matching catches the careless attacker rather than the careful one — but it is a real gate that has blocked real things.
- Publishing is attested. Releases go out with
npm publish --provenance, so an artifact can be traced to the workflow run and commit that built it. That proves origin — it does not prove the code is benign. - Main is protected and every change is a PR. There is no path from an issue to a release that skips a reviewed diff and CI.
- The fixing agent does read attacker-controlled text. That is the entire point of a bug report. Instruction-versus-data separation inside a single context window is not a solved problem, and I don’t have a solution that isn’t just “hope the model behaves”.
- A reviewer’s diff can be truncated. Both gates cap the diff they embed (default 1,000 lines) and tell the reviewer to read the rest directly. A payload deliberately placed past that cutoff is, at minimum, less likely to be read.
- The blast radius is the feature, and no amount of review changes that arithmetic.
- The blast radius is the feature. Same-day fixes and an hourly auto-updater are the same mechanism, and it does not distinguish a good commit from a bad one. The thing that makes a fix reach everyone by dinner makes a compromise reach everyone by dinner.
What writing this section changed
Documenting a system turns out to be an efficient way to audit one. Drafting the paragraphs above surfaced three gaps that were real, and they were closed before this post went up rather than described as future work:- A SHIP verdict is no longer unconditional. Both gates now refuse to pass a diff that touches
the supply chain —
package.json, any lockfile,pyproject.toml, anything under.github/, or the publish scripts — unless a human explicitly acknowledges it. Those paths were previously reviewed exactly as carefully as a comment fix. - A truncated diff no longer counts as a reviewed one. Both gates cap the diff they embed. A pass over a diff we know was clipped now downgrades to the hold state, because “the reviewer was told to go read the rest itself” is not evidence that it did.
- Dependency advisories are now tracked. Neither repository had Dependabot, CodeQL or an
npm auditstep. The existing scans look for malicious patterns in our own code; nothing was watching for an advisory filed against something we already depend on. Both repos now run weekly grouped updates across every ecosystem that reaches a user — including GitHub Actions, since a compromised action runs with the credentials of the one job here that can publish.
#1524 in its changelog resolves to a real
issue number in this repo. It now skips bot-authored PRs outright, because the author field is the
one thing a crafted body cannot spoof.
What remains unfixed is the hardest one: the fixing agent still reads attacker-controlled text,
because that is what a bug report is. Separating instruction from data inside one context window
is not a solved problem, and I’d rather name the residual risk than imply a guarantee I can’t back.
If you are building something similar, inherit the paranoia along with the pipeline — the autonomy
is the easy part.
Why an independent project builds this at all
comfyui-mcp shipped its first npm version on 15 February 2026. The officialcomfy-mcp package
was first published on 31 July 2026 — five and a half months later. You can check both with
npm view <package> time.created; I’d rather cite something verifiable than claim a head start.
That gap is the whole context for this post. An independent project that arrives first does not get
to compete on domain authority, a marketing budget, or a place in the official docs. There has been
no ad spend here — not as a strategy, just as a fact; the traffic in the section above is search
results, GitHub, and people telling each other. That advantage is not durable and I don’t pretend
otherwise: authority accrues to whoever owns the domain, eventually.
What an independent project can compete on is the thing this whole post describes. Nobody with a
larger budget is going to out-care you about whether a Windows account with a locked-down Task
Scheduler can start a background process. The pipeline is the answer to being outgunned everywhere
else: if the fix for the bug you filed can be triaged, written, adversarially reviewed, verified on
real hardware and published while you are still reading the issue thread, that is a form of
competition that headcount doesn’t straightforwardly beat.
None of this is paid work. It exists because people use it and it helps them, and because a bug
report answered by a shipped release the same day is a genuinely satisfying thing to build. The
metrics section above is careful about what it can and cannot claim. This part isn’t a metric at
all — it’s just the reason the rest of it gets maintained.
What this does not do
It does not remove judgement; it relocates it. A human still decides what is in scope, what is parked, and when a “fix” is really a product decision. The pipeline’s job is narrower: make sure nothing is silently dropped, nothing merges unreviewed, and nothing ships unverified. And it fails in instructive ways. The same day this post describes, a fix merged while its gate was still running — landing three P1s in a published release that the gate had already found. The pipeline was right; the sequencing wasn’t. The gate is only a gate if you wait for it.”This is just AI slop”
Someone is going to say that, so let’s take it seriously rather than get defensive about it. The literal claim is true and there’s no point pretending otherwise: this code is largely written by agents. Vibe-coded, if you like. Conceded — including the parts of this post you’re reading. But “slop” isn’t really a claim about authorship, it’s a claim about attention. The failure mode people have in mind is generated code that nobody checked: plausible-looking, superficially reviewed, accumulating in a codebase until it can’t be reasoned about. That is a real thing and it happens constantly. The interesting question isn’t whether a model wrote the diff. It’s what the diff had to survive. Here’s what it has to survive, and the receipts are all in this post:- A reviewer that says no a lot. Not a rubber stamp: on one small Windows fix, successive rounds turned up six P1 defects across three rounds — and two of those were introduced by the fixes for the previous round’s findings. The system’s most-used output is “not yet”.
- Mutation checks, because a passing test is not evidence. Break the fix; if the suite stays green, the test was decoration. This routinely catches wiring that no test actually reaches.
- Live verification against real hardware, and for releases, against the published tarball — not the CI run that produced it.
- Terminal states, so nothing is quietly abandoned. 957 PRs merged and zero left open is not a boast about volume; it’s the absence of a junk drawer.
- And the thing that produced this very post’s security section: writing the system down caused three real gaps to be found and closed before publication.