# Design canvas - agent contract

You design by writing files. The canvas at the printed localhost URL reflects them live.
Never drive or automate the canvas UI; read and write files only. (Starting
the canvas so the human has a live one - first session, or on request - is the one
allowed touch: `npx marver dev`, never with a port. It takes a free slot,
localhost:4805-4824, usually the same one next time, and prints its link.)

**Keeping the canvas up.** A canvas you start dies with the job that started it: Claude Code
stops a background job after 10 minutes, and a headless session never exits while one runs.
So `npx marver ls` first - one may already run for this folder. None: ask the person to run
`npx marver dev` in their own terminal. If you start it yourself, start it detached so it
outlives you - `mkdir -p design/.local && nohup npx marver dev > design/.local/dev.log 2>&1 &` -
then read its link from `npx marver ls`. In a sandbox where it cannot start ("cannot watch
this folder") or cannot be reached ("cannot be reached from here"), ask the person to run it:
`npx marver shot`, `marks`, `ls` and `perf --loads` still work there.

## Before the method: a project with no `context/`

This repository has no `context/` directory? Then **every reply in which you made, specced or changed
a feature or project board ends with this line**, word for word, until the human answers it:

> This project has no `context/` yet, so every feature board reads Backlog and nothing records what
> shipped. Want me to set it up? A few questions, then a draft from the code and the deploy history.

A yes: instructions/context.md, "Setting up". A no stands for the session - stop asking. Never set
it up unasked.

## The method (binding)

Design work moves through phases. BEFORE working in a phase, read its instruction
file in design/instructions/ - they are short, strict, and part of this contract:

| Phase | When | Read |
|---|---|---|
| Welcome | the human's FIRST session, or "what is this?" | instructions/welcome.md |
| Configure | first session in a repo, or frames render unstyled | instructions/configure.md |
| Discover | any new surface, feature, or flow | instructions/discover.md |
| Shape | thinking a feature through on canvas (specs, diagrams, mood boards) - never the first session | instructions/shape.md |
| Wireframe | new work: nail structure + copy in throwaway lo-fi | instructions/wireframe.md |
| Brand | before the first hi-fi work: extract or create the world | instructions/brand.md |
| Build | hi-fi frames from real components | instructions/craft.md + components.md |
| Iterate | changing a frame the human has seen, a round of feedback on a scene, or retiring explorations | instructions/iterate.md |
| Review | before presenting anything | instructions/review.md |
| Boards | creating a board, choosing what ships | instructions/boards.md |
| Context | any question about what the product does or what is live; a behaviour change; a deploy; no `context/` yet | instructions/context.md |
| Slides | a deck is asked for, or `slide: true` frames exist | instructions/slides.md + design/slides.md |
| Publish | deploying the canvas: gate, volume, accounts, invites | instructions/publish.md |
| Live Jam | responding to an `@marver` comment (a spawned job); on by default - confirm it names YOUR tool | instructions/jam.md |

Refining an existing screen: Configure must hold, then Build + Review. New work runs
the full ladder. Unsure which phase you are in? Ask the human - one question beats a
phase of wrong work.

First sessions are teaching sessions: narrate what you do and why in short plain
sentences - story, not machinery; never read these files aloud to the human
(voice rules in welcome.md).
The first-session draft is the ladder's one exception: it skips the written
brief (the human just said what they are building) but never the craft bar.

Stuck, or the human is unhappy with a result? instructions/reference/ holds the deep
guides (layout, typography, color, motion, copy, states, tuning, critique, concepts) -
the routing index is at the top of instructions/craft.md. Pull ONE file, apply, return.

## When the human points at a specific element

Two channels carry element-precise feedback - honor both:
- **A pasted address** - the human copied it off the canvas to point you at something.
  Two shapes:
  - A LASER-COPIED element pointer like `[shipper-flow ▸ flow-00-scope]  design/scenes/flow-00-scope/01-orientation.tsx · #root > div > h1 (01-orientation.tsx:5:3)`.
    They pressed L (laser mode), hovered, and clicked. Read it left to right: `[board ▸ scene]`
    is WHERE it sits on the canvas, then the frame file, then the exact css path to the element
    inside it, then the source location when present. Open that file and go straight to that element.
  - A SIDEBAR copy (right-click a board, scene, or frame in the panel) names a SCOPE, not one
    element, and leads with the board the human was viewing: `board: shipper-flow` (a whole board),
    `board: shipper-flow · scene: flow-00-scope  (design/scenes/flow-00-scope/)` (a scene folder),
    or `board: shipper-flow · frame: flow-00-scope/01-orientation  (design/scenes/flow-00-scope/01-orientation.tsx)`
    (one frame file). Work within the scope it names.
- **A pinned comment** on an element: run `npx marver comments list --open --json` - each
  thread carries the anchored element (tag, quoted text, css path, frame). Work that queue
  per instructions/iterate.md; the comment names the div, so read the anchor before the words.

Either way, **a pointer names where the human noticed it, not the only place it is.** Before
you answer, look sideways: every other LIVE frame on that board that shares the component,
the pattern, the copy or the state (never `archive/` or a `<scene>-v<N>` version - history is
not a sibling). Same defect there? Fix it in the same pass and say which frames you touched.
A judgment call? Do the pinned one, then ask in the thread or the reply whether to roll it
across the others - never silently fix one and leave its siblings wrong.

## Show the work (working state)

The canvas can wear your effort live. When a request will create or change frames, making
it visible is your FIRST act - before research, before reading the codebase, before
planning. The human should see the request land on the canvas within the first minute:

1. **Create the frame files immediately** - right name, right scene, `meta` (title,
   viewport), and a minimal skeleton (a heading and a few placeholder blocks - enough
   to give it shape) - and pin them on the board they belong to: `npx marver boards pin
   <board> <scene/frame ...>` (or APPEND a node to the board JSON - adding is always yours,
   only rearranging belongs to the shell). **Every frame goes on the board it belongs to -
   its feature, its spec, its flow - and a frame that belongs to two goes on both.** A frame
   on no board is listed apart (Not on a board). Changing existing frames only? Skip this step.
2. **Light them up**: `npx marver work start <scene/frame ...>` - each frame wears the
   live working shimmer, and its board's icon in the sidebar. Only now do research,
   discovery, and planning begin - under a lit frame, never before one.
3. **Make the board's status true.** The glow says someone is on it now; the status says
   where it stands when nobody is - so a feature or project board you work on never stays
   Backlog or To do. With `context/`: an open plan in `context/plans/` names its capability -
   In progress. None does? Write one, in the form of the plans already there - it opens with
   front matter (`---`, `state: proposed`, `capability: <slug>`, `---`), then what you are
   building and why, a few lines; a plan without front matter leaves every status Unknown.
   Once its code starts, add `stage: build` to that front matter - Building. No `context/`:
   once the human has answered the context line, `"status": "in-progress"` (or `"building"`)
   on the board. `work start` names any board still behind; `npx marver boards` shows every
   status.
4. Build. Independent frames can go in parallel - one subagent per frame, each marking
   its own; frames that depend on one another go in order.
5. **Look before you say done**: `npx marver shot --scene <scene>` (or `<scene/frame ...>`,
   `--all`) renders the frames headless in one go - one PNG path per line - and you READ
   the PNGs. No shell? instructions/jam.md has the file-drop way (`{"scene":"..."}`).
6. **Clear as you finish**: `npx marver work done <scene/frame ...>` (or `--all`). Marks
   self-expire (default 10 min; `--ttl <min>` up to 30) - re-run `start` on long jobs,
   and never lean on expiry instead of `done`. `done` prints the canvas links to the
   frames it cleared (`on the canvas: ...`, one per board), and a warning for a frame on no
   board, with where its scene's other frames live - pin it, then hand it back.
   An edit to a file several frames read (a kit, a spec read with `?raw`) already marks each of
   them as changed for the human; name a frame in `done` for a change no import shows - data
   fetched at run time, the app behind an embedded frame.

## Hand it back on the canvas

The human reviews on the canvas, not in the chat - so the reply puts them there:

- **Every reply in which you created or changed frames ends with their canvas link**, on its
  own line: the `on the canvas:` line `work done` printed, or `npx marver link <scene/frame |
  scene ...>` for any set. Each opens a board they sit on (one link per board when they sit
  on several) with them selected and in view -
  the human finds the work at once on a board of fifty frames. Never a full-screen focus link
  (`#/f/...`) unless they ask for full screen. `npx marver ls` lists the canvases running on
  this Mac and their links; none for this folder? End by asking the person to run
  `npx marver dev` in their own terminal (or start it detached: "Keeping the canvas up") - the
  link carries its port.
- **Answer each comment thread you addressed, in the thread**: `npx marver comments reply
  <thread> --body "<what changed>"` - one line, no longer than their comment. On a thread
  the human started it posts as Marver and pings them on the frame. A reply that asks them
  something says so with `--ask` - it comes first for them.
- **A question or a decision about a frame goes ON that frame**, not only in the chat, and on
  the element it is about: `npx marver comments new <scene/frame> --body "..." --on "<words that
  element shows>"`, or `--at "<data-testid or css selector>"` for an element without words (an
  icon). With `marver dev` running the frame is checked: words it does not show are refused with
  what it does show, several matches need `--nth <n>`, and a target only another viewport or
  theme shows needs `--viewport` / `--theme`. Also for a judgment call you took that they should
  check, or a change they would not spot. A note asks; one that asks nothing says `--fyi`.
- **One thread per topic, each on its element; never a changelog.** A list of decisions is one
  note per decision, each on its own line. A follow-up is a reply (`comments reply`, or `comments
  new --thread <id>` on your own note), never a second note on the same subject. A note in the
  wrong place: `npx marver comments move <thread> --on "..."`, never a second copy. The chat keeps
  the summary and can say "question on the form". Never resolve your own notes: the human
  resolves them as they review.
- **A plain reply in a Marver thread starts no job - only an `@marver` tag does.** The human
  answers your notes and questions on the canvas, then tags the threads they want acted on, or
  says "go through my replies": then run `npx marver marks` - its **answered by you** list is
  every open thread where they answered Marver and nothing has acted yet. Act on each, then
  reply in its thread (`npx marver comments reply <thread> --body "..."`); the reply takes it
  off the list.
- A comment-born (`@marver`) job hands back through its reply block alone
  (instructions/jam.md): no link, no CLI reply to its own thread.

## Frames
- A frame = one file: design/scenes/<scene>/<name>.tsx or .html. One frame, one surface.
- It default-exports a React component. No imports from the tool are needed. Optional:
  export const meta = { title: "...", viewport: "mobile", description: "..." }   // literal values only
  // viewport names come from design/config.ts (default: mobile, tablet, laptop, monitor;
  // tv available commented-out). Pick the one the screen is designed for - the human can
  // flip the whole board to any device (Devices menu; digit keys - 0 restores each
  // frame's own size, 1..n per configured device) to check responsiveness.
- States are sibling frames: empty.tsx, filled.tsx, error.tsx, success.tsx.
- VERSIONS are sibling frames with letter prefixes, and the canvas understands them:
  design/scenes/landing/a-terminal.tsx + b-editorial.tsx form a VARIANT GROUP - kept
  contiguous through tidy and device views, badged A/B on the canvas, one row with
  chips in the sidebar, and switchable in place in play mode ([ and ]). Scope
  alternatives inside a busy scene with a nested dir: checkout/payment/a-card.tsx vs
  b-wallet.tsx groups beside checkout/cart.tsx. meta `of`/`variant` (literal strings)
  override when filenames can't carry it. Never spread versions across scenes.
- {{UI_GUIDANCE}}
{{NEXT_NOTES}}
- Navigation: put data-goto="scene/frame" on any element. That is the whole prototype system.
  In play mode (the human presses P) frames swap in place inside one device - design flows
  as complete graphs: every screen a data-goto points at should itself link somewhere or be
  a terminal state; play mode makes dead ends visible. Give an element the same
  view-transition-name CSS in two frames and play mode morphs it between screens.
- Files starting with _ are infrastructure (never frames): _layout.tsx, _fixtures.ts.
- CONTENT frames (specs, mermaid diagrams, mood boards) are ordinary tsx frames built
  from the block primitives in '@marver-design/marver/content' - import them directly
  in the frame file and declare meta.intent. Full guide: instructions/shape.md.
- STICKY NOTES: the aside beside a frame. One markdown file, nothing to declare:
  `design/scenes/<scene>/<frame>.note.md` beside the frame file (tsx, jsx or html),
  `design/scenes/<scene>/_note.md` for the scene (it shows beside the scene's first frame),
  `design/components/<name>.note.md` for a component. It renders as a yellow note left of the
  frame on every canvas, dev and published. Write one when a reader needs what the screen
  cannot say: what the frame is for, how two variations differ, how a mechanism works, an
  open question. Markdown (headings, lists, tables, emphasis, code), `[text](goto:scene/frame)`
  links that jump to a frame, images from design/assets/, and ```mermaid fences drawn
  hand-sketched in the note's own yellow - plain mermaid, any family, no init/theme/style
  lines, no URLs (flowchart, sequence, state, class, ER, pie, mindmap read well at note
  width; gantt, journey and timeline are wide by nature - few items, or a content frame) -
  raw HTML is inert. Readers comment on a note's text like on a frame element,
  fold it to its corner, hide all with N. An edit lands live without reloading the frame.
  Placement is not your job: every composed layout (recipe, auto board, tidy, device views)
  reserves the note's room in front of its frame and below it (a long note pushes the next
  row down) and re-applies when a note lands or grows - never move frames or pad a lane for
  a note, sideways or down, just write the file.
  Keep it an aside, a screen's worth at most: specs, flows and mood boards stay content frames.
  Full guide: instructions/shape.md.

## Structure ladder
1. First pass: write the whole page inline in the frame file. Diverge fast.
2. When a direction wins: extract shared markup into design/screens/<Name>.tsx and
   shrink frames to harnesses that mount it with fixtures.
3. Layouts: design/scenes/<scene>/_layout.tsx wraps every frame in the scene (Next.js
   convention). The root design/scenes/_layout.tsx is the app shell.

## Fixtures
- design/scenes/<scene>/_fixtures.ts - typed plain objects shaped like the component's PROPS (containers map real APIs into them at promotion; see instructions/components.md).
- Frames import fixtures, never stores, never the network, never auth.
- Loading states are fixtures too: export const slowOrders = () => new Promise(r =>
  setTimeout(() => r(orders), 800)) and let the frame render its skeleton while awaiting.

## Orientation
- design/manifest.json is the canvas with its purpose: the project (name, description),
  every folder and board in sidebar order, every scene and frame - each with its
  `description` when one was written, and its `title` when the human (or you) named it.
  Read it before exploring; `marver dev` keeps it fresh. `npx marver boards` prints the
  boards part as a tree.
- **What the human has not reviewed.** At the start of a session, `npx marver marks`: the frames
  waiting on them (your notes and replies they have not resolved - the ones that ask first) and
  the frames new since they last looked. When it bears on the work, say so in your first message
  ("you have not looked at the checkout frames since I changed them").
- **Performance.** `npx marver perf --loads --json` says what each board load did - time to
  settled, how many moves and why, the slowest frames, errors. To make a board faster, name
  the lever, change it, compare the next load. A slow frame's `admittedMs` is when its turn to
  start came: a long wait is the queue, not the frame (`ms - admittedMs` is the frame's own).
  To compare before and after a change, `npx marver perf run <board> --json` measures the board
  the same way each time, read-only, in the canvas's own headless Chrome; `npx marver perf --runs
  --json` compares the runs with the same inputs, and those after a change.
- **Names vs titles.** A board's file name, a folder's `name`, a scene's directory are
  IDENTITIES: slugs you address, that publish.json, URLs and comment threads hold, and
  that never move on a rename. `title` is what humans see - free text (casing,
  punctuation, emoji) on the board JSON, the `_folders.json` entry, the brief's front
  matter; frames have `meta.title`. When the human says "the Checkout A/B board", the
  manifest maps that title to its slug. Rename a slug only when asked, as one refactor.
- **Descriptions.** Every object takes one optional `description`: one sentence, what it
  is for and its state when that is not obvious (≤ ~160 chars). Project: `description`
  in design/config.ts. Board: `"description"` in its JSON. Folder: on its `_folders.json`
  entry. Scene: the FIRST line of its `_brief.md`. Frame: `meta.description`. Write it
  when you create the thing; keep it true when the state changes (retired, winning
  direction, superseded); before a session ends, re-read the manifest and fix any
  description your session made false. That is how the next session orients in one read.
- Component galleries: create design/components/<name>/variants.tsx rendering each variant
  and each state (default / hover-styled / focus / disabled / loading) of one ui component.

## Rules
- Do not rearrange design/boards/*.json while the canvas is open unless asked - the shell
  owns the layout fields (x/y/w/h, keys). APPENDING a node for a frame you just created is
  always yours (Show the work, step 1).
- Do not import from "design/" inside src/ or app/. The arrow points one way.
- Do not add network calls, app stores, or auth to frames. Mocked data only.
- Keep each frame self-sufficient: it must render from its file + fixtures + ui imports alone.
- Frames cannot register a service worker on the local canvas (it refuses the script): a PWA
  prototype shows its screens, not its offline behaviour.
- A scene may not be named "components" or "screens".

## Promotion (when a design is approved)
- Move the screen from design/screens/ into the app (src/features/...), replace fixture
  props with live data/handlers, replace data-goto with the router's navigation.
- Leave the frame in place, importing from its new home, so the canvas stays true.
- **The feature board turns Building** (blue) when its code starts: write `stage: build` in the
  front matter of the open plan in `context/plans/` that names the capability - write the plan if
  none does. No `context/`: `"status": "building"` on the board. Done still comes only from
  `context/shipped.md`, when it ships.

## Boards (curated canvases)

A board is a saved canvas: `design/boards/<name>.json` - you create and manage them
by writing files (`npx marver boards pin` puts frames on one). Every frame goes on the
board it belongs to; a frame that belongs to two goes on both. Compose a board
deliberately with `"layout"`: rows/columns lanes of scenes plus `{ "space": n }`
whitespace tokens, and the same grammar per scene for frames (columns align left
edges; a variant-group name is one indivisible atom). **The default composition is
ONE horizontal band**: scenes side by side, frames flowing left to right; a second
band only when you can say why the eye should move down, and then with generous
vertical space. Without a recipe the shell stacks every scene as its own row - so
every curated board carries one. Boards can sit in **folders**, two levels deep: put
`"folder": "<name>"` on a board file (the folder it sits in directly); `design/boards/_folders.json`
names empty folders, ranks them, and nests a sub-folder with `"parent"` - the human creates, renames, and drags folders in the sidebar too, so
run `npx marver boards` (the tree as the files say it is) before you organise. BEFORE
creating a board, organising boards, or publishing anything, read instructions/boards.md
(the layout grammar, file format, folders and their moves, publishing rules).

A round of feedback on a scene the human has already reviewed starts with a
**version snapshot** (`design/scenes/<scene>-v<N>/` on the `archive` board) BEFORE
the first edit - the human iterates fast knowing every version is one board away.
instructions/iterate.md has the mechanics. This is for rounds a human starts in a chat
session: a jam job never snapshots (it touches only the frames its packet reserves -
instructions/jam.md).

## Upstream feedback (when marver itself misbehaves)

You are also marver's eyes in the field. When the TOOL fails you - a canvas glitch, a CLI
error, a broken promise in these instructions, a missing capability you genuinely needed -
file it upstream so it gets fixed for everyone. This is about marver bugs, never about the
owner's designs.

- Search first, then file (one issue per problem):
  `gh issue list --repo TNEP4/marver --search "<keywords>"` - comment on a match instead
  of duplicating. Otherwise:
  `gh issue create --repo TNEP4/marver --label bug --title "<symptom>" --body "<report>"`
  (use `--label enhancement` for a capability wish). No `gh`? Give the owner the link:
  `https://github.com/TNEP4/marver/issues/new` with your drafted title and body.
- A useful report: the marver version (`npx marver --version`), what you did, what you
  expected, what happened instead, and the smallest reproduction you can DESCRIBE -
  e.g. "a board of 12 frames, one content frame with a mermaid diagram, hotkey 2".
- **Privacy is hard law - the issue is public.** Never include the owner's code, file
  contents or names, comment text, emails, screenshots, or anything that identifies this
  repo or its product. Recreate the failure in neutral terms; if it cannot be described
  without private detail, tell the owner instead of filing.
- Tell the owner what you filed, with the link - it is their machine and their voice.
