The critic works on anyone’s summary.
Part 1 built a loop that checks its own synthesis. Part 2 asked where it should live — and the research overturned the answer. One MCP server instead of a plugin; a real run laid out on a FigJam board by Figma’s own server; and the critic turned on FigJam AI’s summary of that board, which failed nine claims out of nine. Then the package that would have been broken on install, caught before it shipped.

A tool nobody can reach is a portfolio piece. The plan was a FigJam plugin. The plan was wrong.
One MCP server, bring-your-own-key, that any agent host can call — and a board tool that hands the host a layout instead of holding Figma credentials. The critic exposed on its own, so it can check a synthesis Motif didn’t write.
The plugin was the wrong surface
Forty searches, primary sources, one afternoon. Four findings changed the plan. Figma’s own MCP server already writes to FigJam — stickies, sections, connectors. One MCP server reaches Claude Code, Cursor, Claude Desktop, Figma Make’s connector slot, Lovable, Notion agents, and Miro, which has a hundred million users and wasn’t on the list. Every research platform is converging on “citations” as a feature, so the receipt is becoming table stakes and the adversarial reader isn’t. And nobody anywhere said “I’d pay for a verifier”; people pay for repositories and for deterministic checkers.
- 01MCP first, plugin laterA FigJam plugin reaches FigJam. A server reaches everything.
- 02The critic is the productReceipts are the ticket in; the second reader is the reason to stay.
- 03Bring your own keyNo paid tier yet means no ETOT-paid runs. Standard for free tools; measured as friction later.
- 04Weave, closedMedia generation, no API, no loop. A distraction, as parked in week one.
The synth is the engine. MCP is the plugin format.
Prove the write path on a real board first
The whole reorder rested on one claim: a host can write a Motif run onto a FigJam board through Figma’s server. If that was flaky or gated, the plugin moved back up. So it was tested before a line of Motif code.
Figma’s write path needs the remote server and OAuth; the desktop server is read-only. After the authorisation click, one call created a section, three colour-coded stickies and a labelled connector on a throwaway board and returned five node IDs; a read-back found all five inside the section.
- 01Motif never touches FigmaThe board tool returns a layout and scripts; the host holds the credentials and runs them. Motif stays a pure function of the transcripts.
- 02The FigJam plugin became optionalNot a dependency.
- 03A palette quirkFigma’s documented red came back as a custom colour on read-back; yellow and green round-tripped. The board tool pins its palette to colours that survive the trip.
- 04Beta, and will be paidFigma says so. Recorded as a risk on the ledger.
The tool I shipped couldn’t serve
Part 1’s one-command install worked by shelling out to two scripts and parsing their printed output for the run directory. Fine for a terminal; impossible for a server. The loop wiring lived inside a script’s main. There was no importable engine.
- 01An engine moduleIngest, synthesise, critique, receipts, board, load-run — that the CLI, the server, and any later surface call the same way. The old ingest script became a shim; re-ingesting the corpus produced 31 files byte-for-byte identical to before.
- 02A logger that doesn’t printOn stdio transport, stdout is the protocol. Anything the loop printed would have corrupted the connection. The logger got an emit hook: stderr by default, MCP notifications in the server.
- 03A corpus snapshot per runA turn ID only means something against a corpus, and the CLI had been ingesting to a temp folder no run recorded. Now every run carries its transcripts.
- 04Nineteen offline testsWith a stubbed model, including the server spawned over stdio and called through the SDK client — the test that caught a plain exception arriving at the host as “Error executing tool” with the real message hidden.
An “installed” environment is a claim, not a fact.
Twenty-five of thirty-two, and the gap kept
Before the critic could be called done as a standalone tool, it had to agree with a human. Part 1’s six blind reports went through it two ways: as the original insight JSON, and as plain markdown with no key and no run — the way a FigJam summary would arrive.
| check | agree | reading |
|---|---|---|
| Unsupported, spot-checked insights | 25 / 32 | the citation layer works |
| P-03 — Penni’s dissent missing | 5 / 8 | known dissent gap |
| P-05 — positive guidance case missing | 2 / 8 | never fired on any report |
Accept the measured gap. No instrument changes. Dissent hunting is the next eval’s job, with its own blind re-run.
Two of the disagreements aren’t: one is the same citation failure caught by the receipt layer (quote_mismatch) instead of the model; one repeats the objection the original run had already flagged.
Eight critic calls, 119–286 s each, about $0.33 each.
Accept the gap; don’t tune. Changing the critic changes the instrument, and the instrument was measured in part 1. Dissent hunting is the next eval’s job, with its own blind re-run. The acceptance file records the numbers unchanged and the ruling beneath them.
Fifteen transcripts, one sentence, one board
In Claude Code: “synthesise data/raw/Dataset-2 with motif and open the report.” Thirty-four minutes, $4.88, twenty-three insights, three contested. The session — following a one-page skill — led with the contested items, verified five receipts against the corpus itself, named a theme the critic said was missing, and refused to call any of it settled.
Then: “lay this run out on the FigJam board with the motif board tool.” Twenty-three sections, every script returning node IDs, one layout collision caught and fixed by the host, a node map saved so the board can be updated in place.

Nine claims, no receipts, nine failures
FigJam AI summarised the board. The summary went into motif_critique against the transcripts. Verdict: fail. Nine claims, twenty-one failures, two warnings, a hundred seconds, forty-nine cents.
- verdict
- FAIL 9 claims · 21 failures · 2 warnings · 101 s · $0.49
- by rule
bad_citation9unsupported9missing_counterexample2overconfident1merged_insights1vague_opportunity1
- 01Every claim failed on citationThe summary carries no turn IDs; a claim the critic has to source on the reader’s behalf is unsupported as written. That is the honest verdict, not a bug.
- 02“Betrayal” was one participant’s word — and she never used itThe summary generalised Michelle’s relational concern to all researchers; Lisa wants others to use her data.
- 03A counterexample was sitting in the corpus“Reuse is rare because researchers can’t find data” — Sam describes browsing the UK Data Service and contacting authors.
- 04A hedge hardened into a recommendationPenni’s “I think it would have to come from GSR… I don’t know” became “non-grant sectors need bodies like GSR.” Single source, overconfident.
A claim the reader has to source is unsupported as written.
The wheel that would have been dead on install
The committed package loaded its config from a path relative to the repo root. A pip install has no repo root. The first synthesis call would have raised — and PyPI releases are immutable. Fixed before upload: config ships inside the package, with a symlink so every documented path still works; runs go to ~/.motif/runs outside a checkout; the registry’s ownership marker sits in the README because the registry reads it from the PyPI description.
pip install etot-motif into an empty environment: motif --help runs, twelve critic rules load, motif-mcp completes a stdio handshake reporting five tools. Published. Registry entry io.github.sleepycobalt/motif, active. A one-time token, revoked afterwards.
Still open
One day, one server, five tools, three hosts, nine failed claims that FigJam called a summary.