Keep the panel node-pack in sync with the orchestrator
The orchestrator (comfyui-mcp, from npm) and the sidebar panel
(comfyui-agent-panel on the Comfy Registry, repo comfyui-mcp-panel) ship
separately. Updating one does not update the other. A new orchestrator
driving an old panel fails in confusing ways, such as a bridge command the panel
doesn't implement or a feature that exists in the docs but not in the sidebar,
and those failures are hard for a user to diagnose. This skill closes that gap.
The two rules that outrank everything else here
Never report a sync that did not happen. This whole feature sits on top of
the fabricate-success fixes (#639/#641): ComfyUI-Manager reports its queue
"drained" even when it never enqueued anything, and a .bak-style copy in
custom_nodes can shadow the real panel in the browser. So the only version
you may ever tell the user is the one read back from disk after the fact.
If the tool throws, the sync FAILED. Say so plainly. Do not soften it, do not
retry it into a success, do not report the version you intended to install.
Never move a pinned user. A pin is a promise. If the user pinned the
panel, you warn and stop. You do not unpin for them, you do not "just this
once", you do not sync anyway because the new version is obviously better.
Offer to clear the pin, and act only if they say yes.
Step 1 — Look before you touch anything
install_comfyui(action:'panel', panel_action:'status')
This never errors. Read these fields:
| Field |
Meaning |
installedVersion |
The panel version on disk (from its pyproject.toml). |
sync.requiredPanelVersion |
The highest panel version this orchestrator build needs. |
sync.decision |
What to do — the whole decision is made for you (Step 2). |
sync.summary |
Plain-language explanation, safe to paraphrase to the user. |
pin |
The active pin: { pinned, version, source: "env"|"settings", reason }. |
shadows |
.bak-style copies that shadow the real panel in the browser. |
isDevSymlink |
A developer's symlinked checkout — never ours to modify. |
Do not compute the comparison yourself. sync.decision already accounts for the
pin, shadow copies, dev symlinks, remote/cloud mode, and unreadable versions.
Step 2 — Act on sync.decision
decision |
What it means |
What you do |
meets-floor |
Panel clears the minimum the orchestrator needs. It is not a statement that a newer panel does not exist — nothing on this path knows the newest published version, and most panel fixes ship without raising the floor (#806). |
Nothing, unless the user is chasing a bug. Say "meets the minimum (X ≥ Y)", never "up to date". If they are debugging, add that a newer panel may carry the fix and that the latest is published in the pack's pyproject.toml. |
sync |
Behind, not pinned, nothing ambiguous. |
Step 3 — sync it. |
pinned-warn |
Behind, but pinned. |
Step 4 — warn only. Do not sync. |
blocked |
A shadow copy, or a pin we couldn't read. |
Step 5 — get it unblocked first. |
unknown |
The installed version isn't comparable (nightly, dev, unreadable) and nothing is pinned. |
Report it, don't guess. Offer a deliberate install_comfyui(action:'panel', panel_action:'update') and let the user decide. (If they were pinned you'd have got pinned-warn instead, so unknown never means "quietly ignore a pin".) |
dev-install |
Symlinked dev checkout. |
Tell them to git pull their own checkout. Change nothing. |
not-applicable |
Remote/cloud, or no local ComfyUI. |
Explain the panel is managed on the ComfyUI host. |
Step 3 — Sync (decision: "sync")
install_comfyui(action:'panel', panel_action:'sync')
That single call re-checks the decision at execution time (the pin may have been
set a second ago), runs the update through the hardened, verified path, and
re-reads the pack from disk afterwards. Read the result:
synced: true → it moved. Report verifiedVersion, the version observed
on disk after the op, not the one we asked for. Then tell the user
ComfyUI must be RESTARTED to load it (restartRequired: true); this
never auto-restarts. Now read stillBehind, which is tri-state:
false → the panel provably meets what the orchestrator needs. Done.
true → the update applied but did not close the gap. Say so; do not
round it up to "you're current now".
null → it landed, but the resulting version (e.g. nightly) can't be
compared, so whether the mismatch is fixed is unknown. Say exactly that.
null is not false. Never report it as "you're fine".
synced: false → nothing was changed. decision says why (pinned-warn,
meets-floor, blocked, …). This is a normal outcome, not a failure.
- The tool errored → the sync FAILED. The error text names the cause
(ComfyUI-Manager's stale-3.x silent no-op, a shadow copy, an unverifiable
post-state) and the fix. Relay it. Never describe a failed sync as
"completed with warnings" or "probably fine after a restart".
For a user who is on Comfy Desktop, restart via the Manager reboot endpoint
rather than killing the process.
Step 4 — Pinned (decision: "pinned-warn") — WARN, DO NOT SYNC
The user deliberately held the panel where it is. Tell them three things and then
stop:
- A newer panel that matches their orchestrator exists (
requiredPanelVersion).
- They are pinned (say to what, and where the pin lives, from
pin.source).
- How to get off it, if they want to.
Your orchestrator (comfyui-mcp 0.48.32) expects panel 0.11.28+, and you're on
0.11.3. You've pinned the panel to 0.11.3, so I haven't changed anything.
Want me to clear the pin and update? I'd unpin and then sync; ComfyUI needs a
restart afterwards.
Only if they say yes:
install_comfyui(action:'panel', panel_action:'unpin') # clears the persisted pin
install_comfyui(action:'panel', panel_action:'sync') # then Step 3
If pin.source is "env", unpin cannot clear it, because the pin comes
from the COMFYUI_MCP_PANEL_PIN environment variable. Tell the user to unset it
(or set it to off) in their environment or ~/.comfyui-mcp/.env and restart
the orchestrator. Do not edit their environment for them, and do not report them
as unpinned. install_comfyui(action:'panel', panel_action:'unpin') returns the
still-active pin in that case, and its note says exactly this.
Step 5 — Blocked
- Shadow copy (
shadows non-empty): a dir like
.comfyui-agent-panel.bak-0.11.28 in custom_nodes is also served as a web
extension, and a dot-prefixed name wins by sort order, so the browser may be
loading the old panel no matter what the disk says. Nothing can be verified
until it's gone. Tell the user to move it out of custom_nodes (not only
rename it) and hard-refresh the ComfyUI tab, then re-run Step 1.
- Unreadable pin:
~/.comfyui-mcp/panel-settings.json exists but couldn't be
parsed, so we cannot prove the user isn't pinned, and we refuse to move them
on a guess. Ask them to fix or delete that file (or set
COMFYUI_MCP_PANEL_PIN=off), then re-run Step 1.
Pinning on request
If the user wants to stay on their current panel (they're mid-project, a newer
panel regressed something, they're testing):
install_comfyui(action:'panel')(action='pin', version='<installedVersion from status>', reason='<why>')
version is required. Never invent one. Pass the installedVersion from
Step 1 to pin them where they already are. A pin records intent only. It does
not change what is installed.
While it's set, everything that could move the panel refuses, not only
install_comfyui(action:'panel'). The panel is an ordinary custom node pack, so the generic node
tools are a second door into the same operation, and they are guarded too:
install_custom_node / install_custom_node (action: "update") / install_custom_node (action: "reinstall")
targeting the panel by any spelling: the registry id, the repo name, or a
git URL including ref-carrying forms like …/comfyui-mcp-panel.git@v0.11.28
and …/comfyui-mcp-panel/tree/main. These also route through the verified
path automatically, so the version they report is re-read from disk like
sync's.
id="all", and install_comfyui (action:"update_all"). A bulk update moves the panel along with
everything else. ComfyUI-Manager can't update everything-except-one-pack, so
while pinned these refuse outright. If the user wants the rest updated, either
unpin first or update the other packs individually by id. Say that plainly
rather than quietly unpinning to make all work.
install_custom_node (action: "fix"), panel_install_node and panel_update_node refuse a
panel target outright, pinned or not. They report success as soon as the
ComfyUI-Manager queue drains, which proves nothing, and there's no verified
equivalent to route them into. Use install_comfyui(action:'panel') instead; don't work around
the refusal.
Prefer install_comfyui(action:'panel') throughout. It's the one with status, sync and the
pin.
When to run this at all
- Right after the orchestrator updates (
install_comfyui (action:"self_update"), a fresh npm i -g, or a
version in the ENVIRONMENT line that's newer than last you saw).
- When a panel/bridge command fails in a way that smells like version drift:
"panel is too old", a
graph_*/ui_* command the panel doesn't implement, a
documented sidebar feature that isn't there.
- Whenever the user asks to update, pin, or unpin the panel.
Be proportionate. This is a one-line check. If decision is meets-floor and the
user didn't ask, don't narrate it. Carry on with what they wanted.
The exception is a user who is DEBUGGING the panel. meets-floor is exactly the
state that hides a shipped fix behind an unchanged floor, so there it is worth the
sentence.
Absolute rules
- A sync that didn't move bytes is a FAILURE. Report the thrown error, never
a success.
- Report
verifiedVersion (re-read from disk), never the target version,
never nightly.
- A pin is never overridden, not even "temporarily". Unpin requires the
user's explicit yes.
- Never touch a dev symlink. It's someone's working repo.
- Always say a restart is required after a sync lands; nothing here
auto-restarts ComfyUI.
Sources
- Official: comfyui-mcp panel product behavior (this repo).
- Empirical: sync/mismatch recipes from observed panel vs disk drift.
1---2name: panel-node-pack-sync3description: Keep the ComfyUI sidebar panel node-pack (comfyui-agent-panel) in step with the orchestrator after comfyui-mcp updates. Use this whenever the orchestrator was just updated (install_comfyui (action:"self_update"), npm i -g comfyui-mcp, a new version in the ENVIRONMENT line), when a panel/bridge command fails in a way that smells like version drift ("panel is too old", a graph_/ui_ command the panel doesn't implement, a feature that works in the docs but not in the sidebar), or when the user asks to update/pin/unpin the panel. It checks the installed panel version against what THIS orchestrator build needs, RESPECTS an explicit version pin (warn-only, never move a pinned user), offers a clear way to unset the pin, runs the sync through the verified install_comfyui(action:'panel') path, and reports the version RE-READ from disk. Never claim a sync that did not happen.4---56# Keep the panel node-pack in sync with the orchestrator78The orchestrator (`comfyui-mcp`, from npm) and the sidebar panel9(`comfyui-agent-panel` on the Comfy Registry, repo `comfyui-mcp-panel`) ship10**separately**. Updating one does not update the other. A new orchestrator11driving an old panel fails in confusing ways, such as a bridge command the panel12doesn't implement or a feature that exists in the docs but not in the sidebar,13and those failures are hard for a user to diagnose. This skill closes that gap.1415## The two rules that outrank everything else here16171. **Never report a sync that did not happen.** This whole feature sits on top of18 the fabricate-success fixes (#639/#641): ComfyUI-Manager reports its queue19 "drained" even when it never enqueued anything, and a `.bak`-style copy in20 `custom_nodes` can shadow the real panel in the browser. So the only version21 you may ever tell the user is the one **read back from disk after the fact**.22 If the tool throws, the sync FAILED. Say so plainly. Do not soften it, do not23 retry it into a success, do not report the version you *intended* to install.24252. **Never move a pinned user.** A pin is a promise. If the user pinned the26 panel, you **warn and stop**. You do not unpin for them, you do not "just this27 once", you do not sync anyway because the new version is obviously better.28 Offer to clear the pin, and act only if they say yes.2930## Step 1 — Look before you touch anything3132```33install_comfyui(action:'panel', panel_action:'status')34```3536This never errors. Read these fields:3738| Field | Meaning |39|---|---|40| `installedVersion` | The panel version on disk (from its `pyproject.toml`). |41| `sync.requiredPanelVersion` | The highest panel version **this** orchestrator build needs. |42| `sync.decision` | What to do — the whole decision is made for you (Step 2). |43| `sync.summary` | Plain-language explanation, safe to paraphrase to the user. |44| `pin` | The active pin: `{ pinned, version, source: "env"\|"settings", reason }`. |45| `shadows` | `.bak`-style copies that shadow the real panel in the browser. |46| `isDevSymlink` | A developer's symlinked checkout — never ours to modify. |4748Do not compute the comparison yourself. `sync.decision` already accounts for the49pin, shadow copies, dev symlinks, remote/cloud mode, and unreadable versions.5051## Step 2 — Act on `sync.decision`5253| `decision` | What it means | What you do |54|---|---|---|55| `meets-floor` | Panel clears the **minimum** the orchestrator needs. It is **not** a statement that a newer panel does not exist — nothing on this path knows the newest published version, and most panel fixes ship without raising the floor (#806). | Nothing, unless the user is chasing a bug. Say "meets the minimum (X ≥ Y)", never "up to date". If they are debugging, add that a newer panel may carry the fix and that the latest is published in the pack's `pyproject.toml`. |56| `sync` | Behind, not pinned, nothing ambiguous. | Step 3 — sync it. |57| `pinned-warn` | Behind, **but pinned**. | Step 4 — warn only. **Do not sync.** |58| `blocked` | A shadow copy, or a pin we couldn't read. | Step 5 — get it unblocked first. |59| `unknown` | The installed version isn't comparable (`nightly`, `dev`, unreadable) **and nothing is pinned**. | Report it, don't guess. Offer a deliberate `install_comfyui(action:'panel', panel_action:'update')` and let the user decide. (If they *were* pinned you'd have got `pinned-warn` instead, so `unknown` never means "quietly ignore a pin".) |60| `dev-install` | Symlinked dev checkout. | Tell them to `git pull` their own checkout. Change nothing. |61| `not-applicable` | Remote/cloud, or no local ComfyUI. | Explain the panel is managed on the ComfyUI host. |6263## Step 3 — Sync (`decision: "sync"`)6465```66install_comfyui(action:'panel', panel_action:'sync')67```6869That single call re-checks the decision at execution time (the pin may have been70set a second ago), runs the update through the hardened, verified path, and71re-reads the pack from disk afterwards. Read the result:7273- `synced: true` → it moved. Report **`verifiedVersion`**, the version observed74 on disk after the op, not the one we asked for. Then tell the user75 **ComfyUI must be RESTARTED** to load it (`restartRequired: true`); this76 never auto-restarts. Now read `stillBehind`, which is **tri-state**:77 - `false` → the panel provably meets what the orchestrator needs. Done.78 - `true` → the update applied but did **not** close the gap. Say so; do not79 round it up to "you're current now".80 - **`null`** → it landed, but the resulting version (e.g. `nightly`) can't be81 compared, so whether the mismatch is fixed is **unknown**. Say exactly that.82 `null` is not `false`. Never report it as "you're fine".83- `synced: false` → nothing was changed. `decision` says why (`pinned-warn`,84 `meets-floor`, `blocked`, …). This is a normal outcome, not a failure.85- **The tool errored** → the sync FAILED. The error text names the cause86 (ComfyUI-Manager's stale-3.x silent no-op, a shadow copy, an unverifiable87 post-state) and the fix. Relay it. **Never** describe a failed sync as88 "completed with warnings" or "probably fine after a restart".8990For a user who is on Comfy Desktop, restart via the Manager reboot endpoint91rather than killing the process.9293## Step 4 — Pinned (`decision: "pinned-warn"`) — WARN, DO NOT SYNC9495The user deliberately held the panel where it is. Tell them three things and then96**stop**:97981. A newer panel that matches their orchestrator exists (`requiredPanelVersion`).992. They are pinned (say to what, and *where* the pin lives, from `pin.source`).1003. How to get off it, if they want to.101102> Your orchestrator (comfyui-mcp 0.48.32) expects panel 0.11.28+, and you're on103> 0.11.3. You've pinned the panel to 0.11.3, so I haven't changed anything.104> Want me to clear the pin and update? I'd unpin and then sync; ComfyUI needs a105> restart afterwards.106107Only if they say yes:108109```110install_comfyui(action:'panel', panel_action:'unpin') # clears the persisted pin111install_comfyui(action:'panel', panel_action:'sync') # then Step 3112```113114**If `pin.source` is `"env"`**, `unpin` cannot clear it, because the pin comes115from the `COMFYUI_MCP_PANEL_PIN` environment variable. Tell the user to unset it116(or set it to `off`) in their environment or `~/.comfyui-mcp/.env` and restart117the orchestrator. Do not edit their environment for them, and do not report them118as unpinned. `install_comfyui(action:'panel', panel_action:'unpin')` returns the119still-active pin in that case, and its `note` says exactly this.120121## Step 5 — Blocked122123- **Shadow copy** (`shadows` non-empty): a dir like124 `.comfyui-agent-panel.bak-0.11.28` in `custom_nodes` is *also* served as a web125 extension, and a dot-prefixed name wins by sort order, so the browser may be126 loading the old panel no matter what the disk says. Nothing can be verified127 until it's gone. Tell the user to move it **out of** `custom_nodes` (not only128 rename it) and hard-refresh the ComfyUI tab, then re-run Step 1.129- **Unreadable pin**: `~/.comfyui-mcp/panel-settings.json` exists but couldn't be130 parsed, so we cannot prove the user *isn't* pinned, and we refuse to move them131 on a guess. Ask them to fix or delete that file (or set132 `COMFYUI_MCP_PANEL_PIN=off`), then re-run Step 1.133134## Pinning on request135136If the user wants to stay on their current panel (they're mid-project, a newer137panel regressed something, they're testing):138139```140install_comfyui(action:'panel')(action='pin', version='<installedVersion from status>', reason='<why>')141```142143`version` is required. Never invent one. Pass the `installedVersion` from144Step 1 to pin them where they already are. A pin **records intent only**. It does145not change what is installed.146147While it's set, everything that could move the panel refuses, not only148`install_comfyui(action:'panel')`. The panel is an ordinary custom node pack, so the generic node149tools are a second door into the same operation, and they are guarded too:150151- `install_custom_node` / `install_custom_node` (`action: "update"`) / `install_custom_node` (`action: "reinstall"`)152 targeting the panel by **any** spelling: the registry id, the repo name, or a153 git URL including ref-carrying forms like `…/comfyui-mcp-panel.git@v0.11.28`154 and `…/comfyui-mcp-panel/tree/main`. These also **route through the verified155 path** automatically, so the version they report is re-read from disk like156 `sync`'s.157- **`id="all"`, and `install_comfyui (action:"update_all")`.** A bulk update moves the panel along with158 everything else. ComfyUI-Manager can't update everything-except-one-pack, so159 while pinned these refuse outright. If the user wants the rest updated, either160 unpin first or update the other packs individually by id. Say that plainly161 rather than quietly unpinning to make `all` work.162- `install_custom_node` (`action: "fix"`), `panel_install_node` and `panel_update_node` **refuse** a163 panel target outright, pinned or not. They report success as soon as the164 ComfyUI-Manager queue drains, which proves nothing, and there's no verified165 equivalent to route them into. Use `install_comfyui(action:'panel')` instead; don't work around166 the refusal.167168Prefer `install_comfyui(action:'panel')` throughout. It's the one with `status`, `sync` and the169pin.170171## When to run this at all172173- Right after the orchestrator updates (`install_comfyui (action:"self_update")`, a fresh `npm i -g`, or a174 version in the ENVIRONMENT line that's newer than last you saw).175- When a panel/bridge command fails in a way that smells like version drift:176 "panel is too old", a `graph_*`/`ui_*` command the panel doesn't implement, a177 documented sidebar feature that isn't there.178- Whenever the user asks to update, pin, or unpin the panel.179180Be proportionate. This is a one-line check. If `decision` is `meets-floor` and the181user didn't ask, don't narrate it. Carry on with what they wanted.182The exception is a user who is DEBUGGING the panel. `meets-floor` is exactly the183state that hides a shipped fix behind an unchanged floor, so there it is worth the184sentence.185186## Absolute rules187188- **A sync that didn't move bytes is a FAILURE.** Report the thrown error, never189 a success.190- **Report `verifiedVersion`** (re-read from disk), never the target version,191 never `nightly`.192- **A pin is never overridden**, not even "temporarily". Unpin requires the193 user's explicit yes.194- **Never touch a dev symlink.** It's someone's working repo.195- **Always say a restart is required** after a sync lands; nothing here196 auto-restarts ComfyUI.197198## Sources199200- **Official:** comfyui-mcp panel product behavior (this repo).201- **Empirical:** sync/mismatch recipes from observed panel vs disk drift.