v0.9.0
Changed
-
The agent guard now refuses a
publish_workflowthat would take a broken
draft live.publishalready checked, but the raw MCP tool went straight
through the guard — so an agent could go live around the verb and ship exactly
the breakage the check exists to catch. Both transports (mcp connectand
mcp serve) run the same check on the same shared code.Fail-closed: if the check itself cannot run — n8n unreachable — the publish
is refused too, and the message says the check failed rather than claiming
the workflow is broken. A read that fails almost certainly means the publish
would have failed anyway, and "couldn't verify, so we shipped it" is not a gate. -
Dangling-reference checks now cover all four forms n8n rewrites on a rename
—$('X')(as before) plus$node["X"],$node.Xand$items('X').
Previously only the first was detected, so a rename could strand a$node[…]
call site that nothing reported:preflight,push,testandpublishall
passed it, and it failed at run time instead. The rule is n8n's own — its
rewriter handles exactly these four — so if n8n treats it as a reference, the
guard now does too.This can surface errors in workflows that passed before. A
$node["Old"]
reference to a node that no longer exists is a hard compliance error, which
--forcedoes not bypass. The message quotes the reference as written, so
it is clear which form triggered it. Computed references ($(someVar), a
template literal with${…}) are still left alone — a regex cannot resolve
them, and n8n has the same limit. -
Breaking:
n8n-decanter test <workflow>no longer executes. It used to
fall back to the newest capture underexecutions/and run the workflow for
real on your instance — a directory that is gitignored, so the same commit
behaved differently for different people, and a bare verb had real side
effects. Baretestis now a static tier: it reads the instance's draft,
reports dangling$('…')references, and runs nothing. Pass
--execution <id>or--scenario <slug>for the pinned run, which is
otherwise unchanged. There is no deprecation shim — atestthat still
executed sometimes would keep exactly the ambiguity this removes.The pinned run now also does the static check first, so a draft already known
to be broken is never fired at the instance. -
publishrefuses a draft carrying a dangling$('…')reference.
Previously nothing checked: the compliance guard runs onpush,preflight
andbackup— notpublish— so a task that only renamed nodes never hit a
gate and the break went live. The check reads the draft on the instance
(the readpublishalready makes), not your local folder:workflow.jsonis
a snapshot, so grading it would pass a broken workflow on a stale mirror and
block a legitimate publish from a fresh clone.
Added
-
initnow scaffolds a hook that catches stranded$('…')references right
after a rename. n8n'srenameNodeMCP op rewrites the node name and the
connections only, so the references it leaves behind used to surface at the
nextpush— arbitrarily far from the rename that caused them. On Claude Code
a PostToolUse hook onupdate_workflownow reports them immediately, split
into the two halves and in the order they must be repaired: other nodes'
expression parameters in n8n first, then the code files here, thenpush.It scans for the old name instead of running
preflight, deliberately: the
hook fires before the background snapshot refresh, and until that lands the
snapshot still carries the old name, so every reference still resolves and
preflightwould report clean. Silent when nothing references the renamed
node. The checklist in the scaffoldedAGENTS.mdremains the contract for
every agent — the hook is a reminder, not a replacement.
Fixed
-
Corrected the rename guidance: n8n's
renameNodeMCP op does NOT rewrite
$('…')references. The scaffolded agent guide (and the 0.6.0 release notes)
claimed n8n rewrites connections and$('…')references server-side on a
rename. Verified against real n8n 2.30.7 and 2.33.3: the MCP op rewrites the
node name and the connections only, then reports success with
validationWarnings: []— every$('Old Name')ref is left dangling, both in
Code-node source and in other nodes' expression parameters. The n8n editor
does rewrite them, but in the browser before it saves, so "server-side" was
wrong for that path too. No amount of pulling repairs this;pullfaithfully
mirrors what n8n stored.init'sAGENTS.md/CLAUDE.mdnow describe the real contract, including the
repair order that matters: fix other nodes' expression parameters over MCP
first, then local code, thenpush. The other order loses the code fix,
because a forwarded MCP write schedules a background snapshot refresh whose
pull overwrites unpushed.jsedits. -
A dangling-reference error now says which half it is and where to fix it.
The two compliance errors were near-identical and neither mentioned a rename,
which led toworkflow.jsonbeing hand-edited — turning the check green while
n8n stayed broken.