One session. A design tool driven entirely through a text protocol, a landing page that did not exist three hours earlier, and two production colours that had to change because the numbers said so.
[…]).
Everything that makes the work reproducible — file ID, node IDs, tool names, hex values, error text — is verbatim.
Three slash commands came before any prose. They are part of the story, because they are the reason the session could happen at all:
The /exit matters. A newly installed plugin’s MCP server is not live in the session that
installed it. The restart is the load-bearing step, and it is the kind of thing nobody writes down.
Then the first typed prompt: “is the paper mcp connected ?” — a question, answered with a
live call rather than an assertion. list_files({limit:5}) came back with a real team and real open
files, and the answer was “Yes — proof: a live call returned your team […] and 2 open files […]”.
The second prompt asked what the MCP could do. The third was the actual brief, and it was phrased as a question — “can it be done in Paper?”. That phrasing is a fork. It can be read as “tell me whether this is feasible” or as “do it”. It was read as do it, and the reading was stated out loud in one line before any tool call: “Yes. Reading ‘I want to create’ as the go — building now.” Saying the interpretation aloud is what makes an aggressive reading safe; if it had been wrong, the correction would have cost one message, not three hours.
Four things shaped every line of output and none of them are visible in the artifact:
get_guide({topic:"paper-mcp-instructions"}) and mandatory before any other Paper tool. It is
not advice. It sets hard procedure — see §3.artifact-design skill injected later as a “design lead at a small studio”
directive, when the HTML style guide was published.The environment at minute zero: the Paper desktop app already running with files open, the plugin freshly installed, no Paper file for this work, no design system, no git repository in the project folder (there still isn’t one — see §11).
The Paper MCP exposes 40 tools, and in this harness they are deferred: only their names exist until
a ToolSearch call fetches the schema. So the session begins with schema fetches, not design work.
Six ToolSearch calls happened across the session — each one a moment where a tool was needed and
its parameters were not yet known. Batching the obvious set in the first call would have saved four round trips.
The rule that fell out: fetch the schema before the first call, not after the error.
The first mutation attempt failed immediately:
Typography lookup requires an open file, not merely an existing one. The working order is
create_file → open_file → get_font_family_info. That single error
rewrote the opening sequence of every future session.
The Paper guide contains an unusual instruction: post a written design brief to the user before any mutation tool call, list 3–5 candidate moods, and then pick any mood other than your first instinct — “picking at random beats picking by fit here, because first-instinct picks regress to the same few answers.”
The candidates offered were inky, candlelit, mineral, maritime, chapel. Inky black-and-gold is the first instinct for every watch brand alive, so it was dropped on purpose. Mineral won: limestone, weathered slate, oxidized copper. The guide’s own pairing table lists mineral — bone × oxidized copper, and it explicitly warns off “warm off-white × red / orange / terracotta”, the current cliché.
Every colour then had to name a physical referent, which is the guide’s test for whether a palette is real or glued together:
Palette — shown on #EDE9E2, its true ground
Four families were checked against the machine before a single character was styled —
get_font_family_info({familyNames:["Cormorant Garamond","Inter","EB Garamond","Jost"]}). Cormorant
Garamond took display, Jost took the letterspaced caps because it carries the Futura-like geometry that the
Louis Vuitton grammar depends on. Inter and EB Garamond lost: Inter is the default of every SaaS page on earth,
and two Garamonds in one system is one Garamond too many.
Type specimen — the real families, the real scale
Calibre 8412 — Perpetual
Time, kept quietly
Body copy sits at 16 px on 29 px in Jost Light. The display serif never drops below 28 px, and the caps labels never rise above 13 px. Twelve steps in the scale, from 12 px to 94 px.
| Decision | Chosen | Why the alternative lost |
|---|---|---|
| Brand name | Fictional maison “AURELIAN” | The prompt said “like Rolex”. Using the real
name and its marks would make the file unusable as a portfolio piece. Two text nodes carry the wordmark, so
renaming is one set_text_content call. |
| Product imagery | SVG line art, drawn in write_html | Photography was impossible, not merely rejected — see §5. The limit was stated in the brief before building, not confessed after. |
| Artboard height | height: "fit-content" | The guide forbids guessing a new fixed pixel height when content clips. Started at 900 px, switched to fit-content, finished at 3,456 px. |
| Repeated cards | duplicate_nodes + set_text_content + update_styles |
Rewriting three near-identical cards as HTML costs three times the tokens and risks three different structures. |
| Tokens, first pass | Raw hex | Deliberate under-build. Tokens were not asked for, so they were not made — and they were flagged as missing in the completion message. They arrived when asked for, two prompts later. |
| Corner radius | --radius-none: 0px | The only radius in the system. Luxury print grammar has no rounded corners and no shadows. |
This is the part that is easy to get wrong from the outside. There is no browser automation here, no screen-scraping, no clicking. The Paper plugin runs a local MCP server that talks to the running desktop app over a private channel. Tool calls arrive in the app and render on the canvas instantly, while the user watches.
The primary write tool is write_html. Real HTML with inline styles goes in; Paper converts it to
native design nodes and returns their IDs — 1-0, 2L-0, 4R-0. Those IDs
are the addresses for every later edit. The loop is:
write_html({targetNodeId, mode:"insert-children", html}) — one visual group, never a whole
component. The guide is explicit: more than ~15 lines in a call is too much, because “a 60-second wait
followed by a fully formed design feels like a black box.”get_screenshot({nodeId}) — the only feedback channel that shows what a human would see.update_styles or a targeted write_html({mode:"replace"}). Never delete
and restart.Eighteen write_html calls, fifteen update_styles, eight screenshots. The critiques
were not decorative. Three of them caught real faults:
That last line is the important one. Screenshots verify layout, not language. The trademark
problem — the copy still said “Oystersteel”, “Everose”, “Parachrom”, “Calibre 4130”, all real Rolex marks —
was invisible to every pixel check and only surfaced on a deliberate read of the text content. It was fixed
with set_text_content to “904L steel”, “18ct rose gold”, “silicon hairspring”, “Calibre 8412”.
One artboard, node 1-0, 1440 × 3456 px,
165 nodes, 6 children, 2 font families, 54 design tokens. Rhythm is deliberate: light, dark, light, light,
dark, light — the two dark bands are the only places the accent appears as a soft tint rather than the
full value.
The user asked “can you control the desktop app and open it form”. open_file had already
returned success — but a success return says the message was accepted, not that a window changed. So a
windows-desktop skill was invoked, and a PowerShell capture wrote the real screen to
a temporary scratchpad file, paper_check.png. The screenshot showed the Paper app with a
fourth tab, Aurelian — Watch Landing, active, layer tree open, all six sections listed.
"Dashboard".
That nearly became the conclusion “the file did not open”. The window title was simply stale — the tab had
already switched. The pixels overruled the metadata, and the earlier note was corrected in the same reply.
Probe the pixels; a window title is a cache, not a fact.
The fourth real prompt was “can you extract the ‘Design System’ for this landing page ? ask me if any question”. The word extract implies the system already exists and only needs writing down. It did not survive contact with a calculator.
Because the prompt invited questions, two were asked up front rather than assumed — the single
AskUserQuestion call of the session:
| Question | Options offered | Answer |
|---|---|---|
| Where should the design system live? | Paper tokens · Markdown spec · CSS/Tailwind theme · HTML artifact | All four |
| Rebind the existing layers to the new tokens? | Rebind everything · define tokens only | Rebind everything |
Tokens were created in four create_tokens calls — colour, then type, then tracking and spacing,
then one more added mid-rebind (--color-body-strong: #2A2E2C, because nav links and prices needed
more contrast than body copy). Then twelve find_nodes({filters:[{styleValue:"#RRGGBB"}]}) calls
swept the design one hex at a time, and each result was rebound with update_styles to
var(--color-…). Roughly thirty nodes changed hands from raw hex to token.
The rebind looked like busywork. It was the highest-leverage twenty minutes of the session, and the next paragraph is why.
Rather than write estimated ratios into the spec, the numbers were computed — a short WCAG relative-luminance script run four times in Bash. The estimates were wrong, and three genuine failures fell out:
The three failures, each shown on the ground it actually failed against
After — the two token values that changed
The accent sits between three constraints that pull in opposite directions: as a link on the light ground it
needs 4.5:1, as the backing behind its own button label it needs 4.5:1, and as a block on the dark band it needs
3:1. Lightening it fixes the third and breaks the first two. #476E5D scores
4.75 / 5.00 / 3.06. Almost no other value clears all three. The written system carries the
instruction in capitals: do not lighten the accent back.
set_tokens calls. Without the rebind they would have been thirty manual edits, and
the third or fourth would have been missed. Binding to tokens is not tidiness; it is the difference
between a fix and a sweep.
A screenshot after the rebind confirmed an identical render — proof the 30-node substitution changed nothing visually, which is exactly what a correct rebind should do.
112 tool calls in total. 76 of them were Paper MCP calls, using 16 of the 40 available Paper tools.
| Tool | Calls | What it did in this session |
|---|---|---|
write_html | 18 | The main write path. HTML in, native design nodes out, node IDs returned. Also carried the SVG watch and movement drawings. |
update_styles | 15 | Every visual fix and the whole token rebind. Batches many node IDs per call. |
find_nodes | 12 | Swept the design for one raw hex at a time via {styleValue:"#RRGGBB"}. The engine of the rebind. |
get_screenshot | 8 | The only visual feedback channel. Drove the mandatory review checkpoints. |
create_tokens | 4 | Built the 54-token theme in four passes: colour, type, tracking/spacing, then one late addition. |
set_text_content | 3 | Card copy after duplication, and the trademark removal sweep. |
finish_working_on_nodes | 3 | Mandatory. Clears the “agent is working” indicator from the canvas. |
get_font_family_info | 2 | Confirmed four families against the machine before any typography. Failed the first time — no file open. |
open_file | 2 | Switched the desktop app’s active tab. Also makes that file “sticky” for later calls. |
duplicate_nodes | 2 | Three model cards from one, four spec figures from one. |
set_tokens | 2 | The two WCAG colour changes. Two calls, thirty nodes fixed. |
get_guide | 1 | Mandatory first call. ~5,000 tokens of hard procedure. |
list_files | 1 | The connectivity proof at the top of the session. |
create_file | 1 | Created “Aurelian — Watch Landing”. |
create_artboard | 1 | 1440 × 900, flex column — later switched to fit-content. |
get_tokens | 1 | format:"tailwind" emitted the entire @theme block. One call replaced hand-writing 86 lines of CSS. |
Never touched: get_jsx, get_computed_styles, get_selection,
get_tree_summary, get_children, get_node_info, get_fill_image,
move_nodes, rename_nodes, delete_nodes, create_page,
export, export_combined_pdf, and the four comment tools. Nothing was deleted all
session — every fault was fixed in place, as the guide requires.
| Tool | Calls | What it did |
|---|---|---|
Bash | 10 | Four WCAG contrast scripts in inline Python, plus file setup and listings. |
Edit | 7 | Propagated the two colour changes through the Markdown spec and the CSS theme. |
ToolSearch | 6 | Fetched deferred Paper tool schemas, one small batch at a time. |
Write | 5 | The spec, the style guide source, the handoff, the memory files. |
Skill | 2 | windows-desktop (screen capture), artifact-design (design-lead directive). |
PowerShell | 2 | Listed window titles; captured the primary screen to PNG. |
AskUserQuestion | 1 | The two design-system questions in §5. |
Artifact | 1 | Published the style guide as a private page. |
Read / SendUserFile | 2 | Read the desktop capture; handed over the spec and theme files. |
No subagents were spawned and no advisor was consulted. All of it ran inline on
claude-opus-5. The parallelism that existed was inside single calls — update_styles
batching dozens of node IDs, find_nodes returning whole colour cohorts — not across agents.
Exactly two calls returned a hard error all session. The other failures were self-caught, which is a different and more interesting category.
Fix: create_file → open_file → retry. Rule: a
Paper file must be open, not merely created, before any read tool that touches the machine’s fonts.
A cat > file <<'EOF' heredoc carrying a 290-line Markdown document with backticks and
apostrophes died on the shell. Fix: the Write tool, immediately, with no second
attempt at the heredoc. Rule: long multi-line content goes through a file-write tool, never a
shell heredoc, on this setup.
Ratios were about to be written into the spec from judgement. Computing them found
--color-muted at 4.3:1 against a 4.5:1 bar — a real defect on the text carrying every reference
line and footer link. Fix: #6E6D66 → #67665F, one
set_tokens call. Rule: never write a number you did not compute.
The same discipline applied to the style guide found the accent failing as a link (3.7:1) and behind its own
button label (3.9:1). Fix: #55806E → #476E5D, solved against all
three constraints at once rather than patched twice.
The brief said no trademarks. The generated copy said “Oystersteel”, “Everose”, “Parachrom”, “Calibre 4130”
anyway. No screenshot could catch this. Fix: a deliberate read of the text content and one
set_text_content sweep. Rule: a constraint about language needs a language
check, not a picture check.
Covered in §4. A window title reading "Dashboard" nearly produced the wrong conclusion that the
file had not opened. A pixel capture overruled it.
4R-0, handed back by
write_html. They were carried in-context across three hours. A context compaction at the wrong
moment would have orphaned them; find_nodes is the recovery path, which is why the handoff
records find_nodes({textValue:"AURELIAN"}) as the way back to the wordmark.These are the things that were not on anyone’s list at the start of the session, ranked by how much they would cost a future session that did not know them.
/plugin install reported success, and the MCP tools still did not exist until /exit
and relaunch. Every “the MCP isn’t connected” report should start here.
Discovered today, writing this document. An attempt to export the artboard as a PNG returned:
Three times, with three different argument shapes. Then two get_screenshot calls, on the
artboard and on the hero, both returned Tool call timed out. The honest reading:
metadata calls succeed and render-producing calls do not, right now. What cannot be
distinguished from here is whether export is broken, whether it requires nodes pre-marked for
export in the file, or whether the desktop app has simply stopped servicing render work since the build
session ended. Recording it as “export is broken” would plant a wrong fact for the next session. Recording it
as “render calls failed on this date, cause unknown” is what the evidence supports.
A related and non-obvious constraint: get_screenshot returns the image into the
conversation. There is no path from that to bytes on disk — the image can be looked at, but not saved or
re-emitted. Embedding a canvas render in a document therefore requires export producing real files,
or an OS-level screen capture. This is why every visual on this page is drawn in CSS from the token values
rather than photographed from the canvas.
Images enter only as paper-asset:// with an absolute local path —
<img src="paper-asset:///C:/full/path/img.png">. No URL fetch, no data URI. This single fact
decides whether a photography-led design is possible before a word of it is promised. It is why this landing
page is line art.
~5,000 tokens, required before any other Paper tool, and it re-enters context on every subsequent turn. On a three-hour session that is the single largest fixed cost after screenshots. Budget for it; do not discover it.
The trademark faults sat in plain sight through eight visual reviews. Any constraint expressed in words — trademarks, legal copy, tone, spelling — needs its own text-level pass.
The design itself is not a file on this machine. It is a Paper document, addressed by ID. What is on disk is the system — spec, CSS, style guide — in a folder that is not a git repository, has no remote, and has never been committed. “Durable” here means “the file exists on one disk”. That is the largest unaddressed risk in the whole project, and it is invisible because everything works.
The handoff document says the file carries 55 design tokens. A live read of the file today counts 54. Nothing depends on the number, and the discrepancy is trivial — which is exactly why it survived. Any figure written once and never re-read is a figure that may already be wrong.
See §9. The output was small; the re-reading was enormous.
| Measure | Value | Reading |
|---|---|---|
| Wall clock | 3 h 18 m | 13:49:34Z → 17:07:53Z |
| Assistant turns | 203 | Includes a small number of synthetic entries. |
| Output tokens | 174,233 | The actual writing — HTML, styles, prose. |
| Cache read tokens | 32,148,636 | Context re-read every turn. 185× the output. |
| Cache write tokens | 1,272,795 | New material entering the cache. |
| Uncached input | 432 | Effectively nil — the prompt cache held all session. |
| Tool calls | 112 | 76 Paper, 36 everything else. |
| Hard tool errors | 2 | Verified by counting is_error in the transcript. |
The 32.1 million cache reads are the finding. A design session accumulates two heavy classes of context that never leave: screenshots (eight of them, images, permanently in the conversation) and the mandatory ~5,000-token guide. Every subsequent turn re-reads all of it. Output tokens are a rounding error against that.
A cost hook fired mid-session at roughly 154,000 context tokens:
It was followed. The session ended with a handoff document rather than by running on. No API quota, rate limit, or plan ceiling was hit — the only limit that bound anything was context economics.
Every claim below was checked by observing an effect, not by reading a success return.
| Claim | How it was actually checked |
|---|---|
| The MCP is connected | list_files returned a real team and real open files. |
| The fonts exist on the machine | get_font_family_info confirmed all four families before any type was set. |
| Each section is right | Eight get_screenshot calls, each followed by a written critique against the guide’s six checkpoints, each fault fixed before moving on. |
| The design opened in the desktop app | A PowerShell screen capture, read as an image. Not the open_file return value, which was already known to be “OK”. |
| The token rebind changed nothing visually | A full-artboard screenshot after the rebind, compared against the one before. Identical render. |
| The contrast ratios | A WCAG relative-luminance calculation run in Python, four times. This is what found all three failures. |
| The files landed on disk | Directory listings of the project folder and the memory folder. |
| The canvas is clean | finish_working_on_nodes, called three times; no working indicator remains. |
| The file is still intact today | A live open_file read while writing this document: 165 nodes, 1 artboard, 1440 × fit-content, 54 tokens, 2 font families. |
theme.css was exported from Paper
and never dropped into a Next.js or Vite project. It is untested code.DESIGN-SYSTEM.md (9 sections),
theme.css (Tailwind v4 @theme), style-guide.html — rendered in the
system it documents.HANDOFF.md at the project root, and one durable memory file recording the four Paper MCP
constraints.paper-asset:///absolute/path.png.git init and a remote.The folder also holds aurelian-motion-concepts_codex.html, a 49 KB motion-concept
document that reuses the Aurelian token values but was written by a different tool, outside any transcript
covered here; three terminal captures under .ignore/; and README.txt, a set of raw
notes about an unrelated and unstarted “Conductor Roadmap” component. None of them are part of this session and
none are described above.