Skip to content

feat(figma): serve node metadata over the bridge and keep the node to convert when a subtree splits - #57

Merged
owjs3901 merged 1 commit into
dev-five-git:mainfrom
forestream:feat/figma-bridge-metadata-and-split-root
Sep 14, 2026
Merged

owjs3901 merged 1 commit into
dev-five-git:mainfrom
forestream:feat/figma-bridge-metadata-and-split-root

Conversation

@forestream

Copy link
Copy Markdown
Contributor

What

An attached Devup Bridge plugin still could not finish a node-scoped export. Two independent things were in the way.

1. get_metadata was the only read left on the metered path

Every other read in a tsx export already has a script, so once the bridge is attached the export spends exactly one metered call — and on a View seat whose allowance is spent, that one call is the difference between a screen and a refusal.

The exclusion (upstream.rs) was justified by response shape, not by data. But find_metadata deserialises a MetadataDocument in its first branch and reduces the official XML to that same type in the next, so a plugin emitting the canonical camelCase document is read by the same decoder rather than a parallel one.

scripts/metadata.js emits exactly that document — { fileKey, version, rootId, nodes[{ id, type, name, childrenIds, descendantCount }] } — and nothing more. No geometry: the decoder does not read it and the snapshot already carries it.

A read with no node id stays remote. That answers with the top-level page list, which is a different contract and not worth imitating.

2. A split subtree never snapshotted the node it was asked to convert

Independent of transport. In accept_metadata, a root with children and more than LARGE_SUBTREE_THRESHOLD descendants is split into child snapshots — and in node scope the root itself is then never a target, so projection fails with DEVUP_FIGMA_NODE_NOT_FOUND on exactly the screen the caller named.

The path is rarely reached because the fast envelope usually serves node scope. It is reached whenever the fast envelope falls back, and then it cannot succeed at all.

Evidence

Measured on a 1920-wide screen whose fast envelope fell back (fallbackReason: textEnvelope):

  • 8 Figma calls, all served by the plugin. No metered call, no rate-limit refusal. The same screen previously stopped at the metadata call and stayed there across 8 retries over 15 minutes.
  • The acquisition call returns in 15–26 ms over the socket.

Boundary

The descendant count a plugin reports is not the count the official XML reports — official metadata collapses instances nested inside an instance, the Plugin API walk does not. The split decision can therefore differ between the two paths. After this change that difference costs one redundant bridged snapshot instead of a failure.

Tests

  • bridge_transport::routing_is_decided_before_the_call — node-scoped metadata is served, a read with no node is not.
  • collector::a_split_subtree_still_snapshots_the_node_to_convert — new; fails before the change.
  • plugin/scripts/verify-generated.mjsEXPECTED carries metadata; the name guard would otherwise only fire at runtime.

Gates

Gate Result
cargo fmt --all -- --check pass
cargo clippy --locked --workspace --all-targets --all-features -- -D warnings pass, zero warnings
cargo test --workspace --release pass except visual_compare::symlink_escape_refused_before_read, which fails identically on an unmodified tree (Windows symlink privilege)
npm run build in plugin/ pass; 13 scripts verified, dist/ rebuilt and committed

… convert when a subtree splits

An attached plugin could not finish a node-scoped export. get_metadata was
excluded from the bridge for its response shape, yet find_metadata reduces both
the plugin JSON and the official XML to one MetadataDocument, so metadata.js
emits that document and the same decoder reads it. A read with no node stays
remote: the top-level page list is a different contract.

Separately, a root with children and more than LARGE_SUBTREE_THRESHOLD
descendants was split into child snapshots that never included the root, so
node-scoped projection failed with DEVUP_FIGMA_NODE_NOT_FOUND whenever the fast
envelope fell back. The node to convert is now always a snapshot target.
@owjs3901
owjs3901 merged commit d5da284 into dev-five-git:main Sep 14, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants