This release closes the gap between what SAFETY.md promises and what the dispatcher actually enforces, fixes a live scope-containment escape, and makes create_component_set genuinely atomic. It also adds an executable safety contract so this class of drift fails CI instead of shipping silently, and folds in two resilience fixes found during live verification.
🛡️ Safety contract closed
node_clone scope escape fixed. Cloning the scope root used to place the clone in its parent — which is, by definition, outside the editable scope — leaving the session with a node it could neither edit nor delete. node_clone now validates the destination parent's scope, lock, instance, and appendability state before cloning. Cloning the scope root itself is no longer possible; re-scope to its parent first.
Guard parity for node_set_effects and create_svg. Both now enforce the locked-layer (and, for create_svg, instance-interior) checks SAFETY.md already claimed. Previously effects could be applied to locked nodes, and SVGs could be created inside locked containers.
Parent-is-instance rule, everywhere. Every parent-gated tool (create_shape, create_frame, create_text, create_svg, create_instance, create_component_set, node_insert_child, node_clone) now rejects a parent that is an INSTANCE, not just one nested inside one.
⚛️ create_component_set is now atomic
Validation runs as a full plan phase before any rename — type, lock, instance, remote status, set-membership, unique variant combinations, duplicate ids, property-value rules, page consistency, and parent scope/name/lock/instance/appendability, including the parent-cycle case. A failed combine restores every original component name. The previous silent reparent skip — an invalid parentId quietly ignored while success was still reported — is now a hard prevalidation error.
🚫 No more orphan nodes
create_frame, create_text, create_svg, create_instance, create_shape, create_component, and node_clone all validate their parent before constructing anything, and clean up the new object if any later step fails. create_instance also rejects COMPONENT_SET ids with a pointer to the set's default variant (fixing a latent TypeError in the process) and replaces its generic error wrapper with a targeted, actionable import-failure message.
🧪 An executable safety contract
A new test suite drives the real dispatcher and mechanically diffs SAFETY.md's gate shorthand against a contract table — in both directions. A gate the docs claim but the code doesn't enforce fails CI; a gate the code enforces but the docs don't mention fails CI too. This is the mechanism meant to keep this exact kind of drift from recurring.
🩹 Resilience fixes (found live)
create_instance no longer hangs on a bad remote key. An unresolvable componentKey could hang past the client timeout and wedge the entire command queue behind it. The import is now bounded by a 15s race, after which the existing actionable error surfaces instead.
variable_delete no longer crashes in documents with variant components. Its consumer scan was reading a property Figma only allows on non-variant components — a pre-existing bug, unrelated to everything else in this release, folded in as hardening once it turned up in testing.
📐 Output-schema conformance
channel_join — and every other tool — now declares an output schema that tolerates extra document-dependent fields. Previously, the SDK could reject a tool's own successful connect payload with a -32602 error.
🔢 Version sync
package.json, the lockfile, server.json, manifest.json, and the plugin's About tab all report 2.3.2, checked by a new check:versions CI script with its own regression self-test.
Cloning the scope root is no longer possible.
create_component_set with an invalid parentId now fails instead of silently ignoring placement.
Backwards compatibility is not a concern for this project (no external end-users), so these contract changes ship without a deprecation cycle.
Details: see CHANGELOG.md and PR #47.
What's Changed
- docs: add IDEO feature + tidy the Core Goals & Philosophy section by @neozhehan in #46
- v2.3.2: Safety Contract Conformance & Atomicity Hardening by @neozhehan in #47
Full Changelog: v2.3.1...v2.3.2