FigJam should start as source, not a write.
The flashy version writes first
The flashiest demo in this category is an agent that drops a finished journey map straight into the team FigJam. Prompt in, nodes appear in the shared file, everyone claps.
We felt that pull too. The engine grew a native Mermaid jam integration (commit 49bbded2, 2026-05-05) precisely so agent output could become FigJam-ready boards, and it shipped on the v0.17 release train alongside the Codex plugin, not as a later bolt-on. The capability to write was there from the start.
The problem is who else is in that file.
Shared design files are social spaces
Code repos have branches, diffs, and pull requests between an agent and the thing everyone depends on. Design files have none of that. They have people, working live, on the same canvas, right now.
That changes the interaction contract completely. An agent should not quietly create nodes, move boards, or replace planning artifacts in a shared file because a prompt sounded design-flavored.
So the rule we shipped: the board starts as local source, and sync is something you do, not something that happens to you.
Born local, with a contract
When Studio was carved out of the engine monorepo (commit 122c969, 2026-05-09), the jam bridge came across as two files: mermaid-board-contract.ts and mermaid-board-surface.tsx, from an engine branch literally named codex/native-mermaid-jam-bridge.
The filenames are the architecture. There is a contract, a typed boundary describing what a board is and what syncing it means. And there is a surface, a local pane in the workbench. The /board slash command focuses the Mermaid Board pane. Nothing about opening, reading, or editing the board touches Figma.
Sync is a state machine, not a side effect
The board tracks lastFigJamSync as an explicit, recorded state, with values like not_sent and synced. Not a spinner. Not an optimistic checkmark that resolves whatever happens. A state you can read back later.
It shows node and edge counts before any write, so you know the size of what would land in the shared file. The write itself happens only through a named action, board.sync_figjam. And when the bridge cannot do the full job, the board surfaces sync.fallbackReason instead of degrading silently.
That last field is the one I would defend hardest. Silent fallback is how trust dies in agent tools: the tool claims it synced, the file shows something else, and the user stops believing every status light in the product. If you fell back, say why, in the interface, in a field with a name.
The lane appears with the work
The board lives in the contextual design lane (commit 7af42a7, 2026-05-13, a 1070-line change across the workbench): planning source shows up when there is planning to show, instead of squatting in the default view as one more dashboard.
After research produces a board, the receipt stays compact:
- source type
- the generated Mermaid
- FigJam-ready status
- sync status
- an open-source action, and a sync action only when the bridge is ready and you approve
A PM board earns that screen space by carrying product decisions: persona pain points, journey steps, assumption clusters, risks, open questions, candidate experiments. Those are design memory. The next Codex or Claude Code run should be able to cite the board source directly, not squint at a screenshot of it.
The boundary has to be visible
Direct FigJam sync is genuinely useful, which is why the write path exists at all. But before any external write, the user should know:
- which file will be touched
- which page or board receives the output
- whether nodes are new, updated, or skipped
- how to retry or roll back
Until a tool can answer all four, local Mermaid plus source export is the right public behavior. That is not a limitation we apologize for. It is the contract.
The principle
Design agents should earn external writes.
The checklist that fell out of building this one:
- Keep a local, typed representation of the artifact (the contract file), so the source of truth is never the shared canvas.
- Make sync a named action a human triggers, never a side effect of generation.
- Record sync as explicit states (not_sent, synced), not transient UI.
- Show the blast radius before the write: node and edge counts, target file.
- When you fall back, name the reason where the user can see it.
Show the source. Show the receipt. Let the designer approve the move into shared space.