/packs / visuals

visuals

A pack for a workspace that wants finished visuals from agents: posters, social cards, covers, slides.

  • git
  • flows
  • agents · crew

As of yardr-packs at commit a2b86e6, 11 Nov 2026. The pack's own files keep the author's words: issue · bead, repo · depot, workspace · yard, agent · crew member, merge queue · assembly, merges · lands.

01 / apply it

yardr pack apply ./visuals --depot <depot>

From a clone of yardr-packs: the pack's directory. yardr pack check ./visuals --depot <depot> shows what it would change first.

yardr pack apply https://yardr.dev/packs/visuals/ --depot <depot>

From this site, as the files below: from yardr v0.8.0 on.

Lives in visuals/ at a2b86e6 · SETUP.md

02 / the flows

One flow.

flow visualbacklog → picked · 4 stages

03 / the files

Every file, as it ships.

# Visuals pack for yardr: the lane a composed visual takes before anyone
# holds the file. A bead of type visual carries a brief that names a format
# (a poster, a social card, a cover, a slide) and its size. A visual designer
# composes three directions at that exact size on the bead's branch and shoots
# each one, and a person looks at the shots, names the direction to hold, and
# holds its file with a PNG at the exact size (and a PDF where the brief is
# for print). Nothing lands.
# Like the starter pack it names no agent product, model, depot, person or
# secret: the import asks, and they stay in the yard. The format is the
# starter's (packs/starter/pack.yaml, docs/packs.md in the yardr repository).

name: visuals

# The kind of depot the pack is for: the directions are committed on the bead's
# branch.
kind: git

# The setup notes, for the operator: the import, the agent it asks for and the
# programs the shots need. pack check and pack apply print them.
setup: SETUP.md

# One flow, for beads of type visual, beside the flow the depot follows for its
# own work (the starter's, say): this pack has none without a type. The type is
# the pack's own because a type follows one flow. The webdesign pack's flow is
# for type mockup, so a pack that reused that type would have its import
# re-point every depot already on the mockup flow.
#
# There is no assembly stage. The directions stay on the bead's branch and the
# shots on its note, and the words of the person's pick name the direction to
# hold: landing all three files would put throwaways on the base, and landing
# one would need another agent to take the other two out. A bead ends at
# picked, where the person holds the picked file with its PNG (and its PDF for
# print) from the branch.
#
# Rounds are not limited. The only way back is a person's move from pick, with
# a note that says what to change, so a person decides each round.
flows:
  visual:
    type: visual
    stages:
      backlog:    { group: backlog,          file: flows/visual/backlog.md,    manual: [directions] }
      directions: { group: visual-designers, file: flows/visual/directions.md, outcomes: { done: pick, question: pick }, manual: [backlog] }
      pick:       { group: decisions,        file: flows/visual/pick.md,       manual: [picked, directions, backlog] }
      picked:     { end: true }

# The visual designers are a group of this pack's own. The webdesign pack takes
# a group of its own for the same reason: a pack names a group together with
# its role, so naming builders here would aim the depot's code builders at a
# designer's prompt, and naming webdesign's designers would aim this pack's
# role at mockup beads. The import makes <depot>-visual-designers and asks
# which agent runs it. One bead at a time: each serves its worktree and runs
# headless Chrome three times. A yard that wants more raises the limit of the
# group it has (yardr group set). backlog and decisions are the yard's two
# people groups, which the import reuses.
groups:
  visual-designers: { agent: roles/visual-designer.md, limit: 1 }
  backlog:          { people: true }
  decisions:        { people: true }

# The designer's role tells it to mail {{yardmaster}}, so the pack names the
# crew member, as the starter does. It hears a bead enter pick, a bead close,
# and the events that need someone. The pack carries no role for it: the
# yardmaster keeps the prompt it has.
crew:
  yardmaster:
    hears:
      events: [attention]
      stages: [pick]
      done: true
summary: a visual bead waits here until its brief names the format, the size and what to say

A visual bead asks for three directions to pick from and hold, not for a page
to ship. Before it is sent to `directions` its body says:

- the format: what the visual is (a poster, a social card, a cover, a slide)
  and where it will be seen;
- the size: for screen, the exact pixels, width by height (1080x1350, say);
  for print, the trim size and which it is (A4, A5, US Letter, say);
- the subject: what the product, place or person is, in one concrete sentence;
- the audience: who sees the visual;
- the job: the one thing the visual gets its viewer to do or feel;
- the words: the copy that must appear, or that the designer writes it;
- what the brief fixes: brand colours, typefaces, a logo, pictures that must
  appear, or that nothing is fixed;
- reference work: pictures or addresses of work the person likes or dislikes,
  each with what to take from it or leave;
- what is not wanted: looks already tried and turned down.

Send it with `yardr bead advance <id> --to directions`.
summary: you compose three directions at the brief's exact size and put their shots on one note

You make directions here, not a finished file. You work on branch `yard/<bead>`
in the bead's own worktree. The bead's body is the brief, and your role says
how to work.

Before the bead leaves as `done`:

- three files `directions/<n>-<name>.svg` or `directions/<n>-<name>.html` are
  committed on the branch, each composed at the brief's exact size and opening
  on its own with no build step;
- beside each, its finished PNG at the exact size is committed
  (`directions/<n>-<name>.png`), and for a print brief its PDF
  (`directions/<n>-<name>.pdf`), taken at the last commit;
- one note on the bead carries the three shots at the exact size, taken at the
  last commit, and says that commit's short SHA;
- the same note gives each file's address on the watch (the address
  `yardr preview watch` printed, then the file name), or what the command
  printed when it gave none; the watch ends when the bead leaves `directions`,
  so the shots are the record;
- the same note names each direction's idea, type pairing and palette, and
  your role's distinctness rule holds for the three.

Outcomes:

- `done`: all of that holds. The bead goes to `pick`, where a person looks at
  the shots.
- `question`: no direction can start without an answer, a size the brief never
  names, say. Note exactly what you need first. The bead goes to `pick`, where
  a person answers and sends it back to `directions`.
summary: a person looks at the shots, names the direction to hold, or sends the bead back

For a person. What to read: the designer's note on the bead, which carries the
three shots at the brief's exact size and names each direction
(`yardr prime --bead <id>` prints the bead with every note in full). The
directions are files that open on their own, on the bead's branch `yard/<bead>`
under `directions/`; the shots are what to judge them by.

A bead leaves `pick` when a person moves it, with words:

- to `picked` when a direction is taken: the bead closes, and nothing lands on
  the base. Name the direction in your words, by its number and name, and say
  what to keep from the others. What you hold is the picked file with its PNG
  at the exact size (and its PDF where the brief is for print), on the branch
  under `directions/`;
- to `directions` with a note when none is there yet: say what to change, and
  the designer composes three again;
- to `backlog` to park it or have the brief reshaped.

A bead also comes here when the designer could not start without an answer: the
note says what it needs. Answer in a note and send it back to `directions`.

When a bead arrives the yard puts a card for these moves on it, and picking one
moves the bead: your words with the pick stay on the bead for whoever works it
next. A bead with an ask already open gets no card, and a card gives way to an
ask put after it: that question is what you answer, and the bead is moved by
hand once it is.

## Ask
kind: moves
question: Which direction do you hold, or where should this bead go next?
context:
- The designer's note holds the three shots at the brief's exact size and names each direction. The directions are on the bead's branch under directions/.
- To hold one, move the bead to picked and name it in your words. To ask for another round, move it back to directions and say what to change.
- Your words with the pick stay on the bead for whoever works it next.
# Visuals

A pack for a yard that wants finished visuals from agents: posters, social
cards, covers, slides. A brief goes in, three directions come out, each
composed at the brief's exact size with a picture of it, and a person picks
one and holds the finished file. Nothing lands. Nothing here is compiled into
yardr; apply it with:

```sh
yardr pack apply /path/to/yardr-packs/visuals --depot <depot>
```

`yardr pack check /path/to/yardr-packs/visuals --depot <depot>` shows what the
import would do first, and writes nothing. SETUP.md is the operator's steps.

The pack adds the bead type `visual` beside the flows a depot already follows.
A depot on the webdesign pack's mockup flow keeps it. One type follows one
flow, and the webdesign pack's flow is for the type `mockup`, so a pack that
reused that type would have its import re-point every depot on the mockup flow.
The type `visual` is this pack's own, and an import leaves every other type's
flow, routes and groups as they are.

## How it hangs together

```
backlog -> directions
directions -> pick on done | pick on question | backlog
pick -> picked | directions | backlog
picked terminal
```

| stage | worked by | what happens |
|---|---|---|
| `backlog` | `backlog`, people | the bead waits until its brief says the format, the size, the subject, the audience, the job, the words, what is fixed, reference work and what is not wanted |
| `directions` | `visual-designers` | an agent commits three direction files, `directions/<n>-<name>.svg` or `.html`, each composed at the brief's exact size, starts a watch on the folder so the person can open the files while they are built, shoots each at that size (and prints a PDF where the brief is for print), and puts the shots on one note that names each direction's idea, type pairing and palette and each file's address on the watch |
| `pick` | `decisions`, people | a person reads the shots and moves the bead to `picked`, back to `directions` with a note, or to `backlog`; the yard puts a card on the bead offering these moves, and the person's words with the pick name the direction |
| `picked` | | the end: the bead closes, and the person holds the picked file with its PNG (and its PDF for print) from the branch |

`question` also leads to `pick`. A designer that cannot start without an answer
(a brief that names no size) says what it needs on a note, and the person
answers there and sends the bead back to `directions`.

There is no assembly stage. The directions stay on the bead's branch
`yard/<bead>` and the shots on its note, and the words of the pick name the
direction to hold. Landing all three files would put throwaways on the base,
and landing one would need another agent to take the other two out. What the
person holds is the picked file with its finished PNG at the exact size (and
its PDF where the brief is for print), on the branch under `directions/`.

**Three directions that differ in idea.** The designer's role asks for three
ideas, each a composition built around something that only this subject has,
and for each a type pairing and a palette. The rule is that no two directions
share the same idea, and a palette alone never carries the difference: one
layout in three tints fails it. A brief that fixes the brand's colours or
typefaces keeps them in all three, and the ideas carry the difference.

**The exact size.** The designer shoots each file with headless Chrome run
directly, at the brief's exact pixels (`--window-size`, `--screenshot`), and
checks each PNG with `file`. The shot script yardr carries (`contrib/shot`)
takes a width only and keeps the height at 800, which is why the role runs
Chrome itself. For a print brief the PNG is the trim size at 96 CSS pixels to
the inch (A4 is 794x1123), and the PDF, printed with `--print-to-pdf` from a
page that carries the trim size, is the print master. A file narrower than 500
pixels is shot through a frame of its exact size, since Chrome on macOS lays
narrower windows out 500 wide.

**The watch.** Once the first file exists the designer starts
`yardr preview watch $YARDR_BEAD directions`. The yard serves the folder's
files read-only through its preview endpoint, and a file reloads in the
browser whenever it changes, so the person sees the three directions take
shape. The command prints the address they open at on its `opens at` line: the
host the yard's edge gave the watch, or the path `/preview/<app>/` on the yard
where the edge gives none. `directions/` holds no `index.html`, so the folder's
own address answers 404, and so does the Open button of the watch's card on the
Previews page. The designer therefore names each file's address, the printed
address followed by the file name, and puts the three on a note as soon as the
watch is up, so the person has them while the designer builds. The note with
the shots gives them again. The watch ends by itself when the bead leaves
`directions`, so the shots are the record of what the person picks from.

## What it needs

- **An agent for `<depot>-visual-designers`.** The import asks for its kind,
  model and permission arguments, as it does for any agent group it makes. The
  group is the pack's own and not the depot's `builders`: a pack names a group
  together with its role, so naming `builders` would aim the depot's code
  builders at a designer's prompt. One designer works at a time, and
  `yardr group set <depot>-visual-designers --limit 2` allows more.
- **The yard's two people groups**, `backlog` and `decisions`, which the import
  reuses when the yard has them and asks for the members when it does not.
- **A crew member called `yardmaster`**, or another that takes the name
  (`--crew yardmaster=<name>`). It hears a bead enter `pick`, a bead close, and
  the events that need someone. The pack carries no role for it, so the
  yardmaster keeps its prompt.
- **On the yard's machine**, for the shots: headless Chrome (Google Chrome in
  `/Applications`, `google-chrome` or `chromium` on the PATH, or a path in
  `SHOT_CHROME`), `python3` and a yardr checkout to read. The directions load
  their typefaces from CDNs, so the machine needs the network while a designer
  works.
- **A yardr with `yardr preview watch`**, for the watch (yardr 26b2a52, 4
  November 2026, and after). `yardr docs watch` prints its page on a yardr that
  has the command and names the pages it has on one that does not. On an older
  yardr `yardr preview watch` prints the help of `preview` and ends 0, so the
  designer goes by the `opens at` line: with none, it puts what the command
  printed on its note and goes on, and the shots are as before. The address is
  the path on the yard until the yard is linked to an edge that gives the watch
  a host (yardr ec29917, 4 November 2026, and after). Where the yard is linked
  and the edge refuses, the command prints the edge's words under the path.

## What the import asks

Beside a yard that has `backlog`, `decisions` and a `yardmaster`: the agent of
`<depot>-visual-designers` (`--bind visual-designers=<kind>[:<model>]`), or the
group of the yard that takes it (`--group visual-designers=<group>`). Otherwise
also the members of `backlog` and `decisions`
(`--members decisions=<a>,<b>`) and which crew member takes `yardmaster`
(`--crew yardmaster=<name>`).

## Files

- `pack.yaml`: the flow, the groups that work it and the crew member its role
  names.
- `flows/visual/`: a stage file for each stage a bead waits or is worked in.
  What a stage needs to be true is here, and how a designer works is in the role.
- `roles/visual-designer.md`: the designer's role. It holds the work in five
  steps, the house style for composed stills, and the exact-size mechanics.
- `SETUP.md`: the operator's cards.
- `test`: the pack's tests.

Files of a pack that `pack.yaml` does not name are not installed. The role and
the stage files point at none of them, because the yard that imports the pack
never has them.

## Runtimes

The pack has no program of its own beyond `test`, which needs `sh` and `grep`.
The designer's steps use `git`, `file` and headless Chrome run directly.

## Testing

`sh .yardr/test` at the root of the repository runs `yardr pack check` and then
`visuals/test`. The test needs no network and no secret and takes well under a
second. It is text, so it pins what a later edit could lose without a sound:

- the role carries the distinctness rule, the exact-size mechanics (Chrome run
  directly, the PNG shot and its check, the print PDF and its check, the frame
  under 500 wide) and the watch (the command with the bead's id, how a file's
  address is made, and the two notes that carry the addresses);
- each stage file has its exit line, `directions.md` names the finished PNG and
  PDF, the role's distinctness rule and the address of each file on the watch,
  `backlog.md` names the size, and `pick.md` has the card of its moves;
- the decisions of `pack.yaml`: the pack's own type and group, no script group,
  the yardmaster hearing `pick` and carrying no role;
- no role or stage file points at a file the import does not install.

## What was checked, and what was not

Checked, in a scratch yard under a short temp directory, with an environment of
nothing but `HOME` and `PATH`, hand-walked throughout so that no agent started
(the trial played designer and person by hand):

- `yardr pack check` and `yardr pack apply` of this directory into a depot, with
  the answers on the command line as SETUP.md gives them. The flow prints as the
  four stages above, the installed role has no placeholder left, and the yard
  reads all three stage files and the card without a warning.
- Checked against a depot that already follows the webdesign pack, the plan
  shows the mockup pointer, its routes and its groups as
  `not in the pack (would be left alone)`, and `backlog` and `decisions` as
  `unchanged`. Against a fresh yard the plan instead creates `backlog` and
  `decisions` and asks for their members, and creates the yardmaster's two
  subscriptions with the `yardmaster` itself `unchanged`.
- Two beads of type `visual` walked from a brief to three direction files with
  shots on a note to picked: one for screen (1080x1350) and one for print
  (A4). Each held PNG is byte-exact to the brief's size (`file` names its
  pixels), with a PDF at the trim size for the print one. A third bead walked
  the `question` outcome to `pick` and back to `directions` with words, and the
  pick card rendered on it with its question and moves.
- The role's exact-size mechanics, step by step as separate commands: the PNG of
  an HTML file and of an SVG file at 1080x1350, `file` naming
  `PNG image data, 1080 x 1350`; the PDF of a print page, `file` naming one
  page and `MediaBox` the trim size; a width under 500 shot through a frame at
  its exact width; a note with three `--file`; and the temp directory removed.
- The test breaks on each of 26 changes made to a copy, one at a time: the
  distinctness rule, each mechanics flag and check, the watch command and its
  address rules, the type, the group, the limit, an added script group, a crew
  role, an exit line, the pick card, and a link to a file the import leaves
  behind.

Not checked:

- No designer agent has run this role or started its watch. Whether one follows
  it to three directions that differ in idea, and how good they are, is not
  known. The first real bead is that test. The watch command was read and not
  run against a bead, so what a person sees in a browser, and whether the
  addresses reach the person in time, is as the docs describe.
- Shots on a machine other than a Mac with Google Chrome. The role looks for
  Chrome and Chromium elsewhere, and has not been run there.

## Left out

- **Generated images and any image model.** A picture the machine dreams up is a
  source pack of its own, later, added on without changing this one. This pack
  composes: type, layout, shapes, charts.
- **A dir depot version**, for people with no repository.
- **Brand kits kept between beads.** Each bead carries what its brief fixes.
- **Video and motion.** A still is what this pack holds.
- **Landing anything on a base.** There is no assembly by design.
- **A printer's bleed past trim.** The file is the trim size exactly; full-bleed
  art runs to its edge.
- **The type `mockup`.** One type follows one flow, so it stays webdesign's.
# Visual designer

You make visual directions for one bead, in its own worktree on branch
`yard/<bead>`. A bead of type visual carries a brief that names a format (a
poster, a social card, a cover, a slide) and its size, and asks for three
directions that a person looks at and picks one to hold. Your product is three
direction files, each composed at the brief's exact size, a finished PNG of
each at that size (and a PDF where the brief is for print), and shots of them:
three, on one note. The person's pick decides what is held.

Nothing ends your session but `yardr bead done <id>` (or `--outcome question`,
closing, or holding).

Nothing here lands, so the depot's test command that `yardr prime` prints is not
part of your exit.

While you work, a line may arrive naming a note added to your bead: read it with
`yardr bead show <id>` and ack it with `yardr bead note <id> --ack <seq>`. A note
by {{yardmaster}} or {{operator}} changes the bead.

## The work

1. **Read the brief.** `yardr prime --bead <id>` prints the bead, its notes and
   the stage file with its exit. Find the format, the size, the subject, the
   audience, the job, the words, what the brief fixes and the reference work,
   and look at each reference you can reach. A bead that comes back from `pick`
   carries the person's note, which says what to change and which directions to
   keep: read it first.
   Decide what kind of size it is: for screen, the brief names exact pixels,
   width by height; for print, it names the trim size. For print, work out the
   PNG's pixels at 96 CSS pixels to the inch (Exact size, below) and state them
   in the plan note. A brief that names no size is a question for the stage,
   not a guess.
   Done when you can say the brief in one line: the format, the size (with the
   PNG's pixels for print), the subject, the audience and the job. The line
   opens the plan note of step 2. Where the brief leaves out the audience or
   the job, propose one and say so. The stage file says when to ask instead.

2. **Choose three directions that differ in idea.** List eight facts about the
   subject that no competitor shares: where its name comes from, the place it
   sits in, its figures, what it is made of and the colours that come with
   that, its daily routines, and the printed objects of its trade (tickets,
   labels, menus, forms). For each direction, write one idea line: the
   composition the direction is built around, and which of the eight it builds
   with, a different one in each. Give each direction a type pairing (the faces
   and what each is for) and a palette of four to six named hex values.
   The distinctness rule: no two directions share the same idea, and a palette
   alone never carries the difference. Three directions that are one layout in
   three tints fail it; three that differ in composition, each built around its
   own fact, hold it. A brief that fixes the brand's colours or typefaces keeps
   them in all three, and the ideas carry the difference.
   Write the plan in a note (`yardr bead note <id>`): the one-line reading, the
   eight facts, then each direction's idea, type pairing and palette.
   Done when the note is on the bead and the rule holds for its three
   directions.

3. **Build the three.** One file per direction, `directions/<n>-<name>.svg` or
   `directions/<n>-<name>.html`, with `<n>` from 1 to 3 and `<name>` a slug for
   the direction. SVG or HTML, whichever the direction wants: type, layout,
   shapes and charts are all composed work. Each file is composed at the
   brief's exact size: an SVG carries the size in its width and height (pixels
   for screen, real units for print); an HTML file fixes its body to the size,
   and for print carries `@page` at the trim size with margin 0. A file opens
   on its own: styles inline, CDN links for typefaces with the version pinned
   in the URL, no build step, and no key, token or account. Write the words
   before the markup, in the subject's own vocabulary.
   Commit each file when it renders, as `<bead id>: direction <n>, <idea>`,
   with no trailers of your own: the yard adds them. Your commits stay on the
   branch: no merge, and no push unless your brief says you may. A bead that
   came back keeps the directions the person kept, and new ones take the slots
   of the others (`git rm` the old file).
   Choose the three file names before you build the first. As soon as the first
   file is there, start a watch on the folder with
   `yardr preview watch $YARDR_BEAD directions` (`$YARDR_BEAD` is your bead's
   id). The yard serves the files of `directions/` as they stand, and a file
   reloads in the person's browser when it changes, so the person sees the
   directions take shape. The command prints the address the files open at, on
   its `opens at` line. The folder itself answers 404, so a file's address is
   the printed address followed by its file name, as in
   `/preview/<app>/1-<name>.svg` for `opens at /preview/<app>/`.
   Put the three addresses on a note at once (`yardr bead note <id>`), so that
   the person can open them while you build; a file opens once it exists.
   If the command prints no `opens at` line, put what it printed on the note in
   place of the addresses and go on. The watch ends by itself when the bead
   leaves `directions`, and `yardr docs watch` says what it serves and what else
   ends it. The watch is for the person to look at; the shots still come from
   your own commands (Exact size, below).
   Done when three files are committed and the note with their addresses is on
   the bead.

4. **Look, then fix.** Shoot each file at the exact size (Exact size, below) and
   open the PNG. Trust the picture over the code: the shot is what a viewer
   sees. Ask three questions of it. Given five seconds, would a stranger know
   what it offers and where to act, or feel what it is for? Could the nearest
   competitor use it as it stands? Which tells does it carry that its idea did
   not choose? List the three largest problems, fix them, commit and shoot
   again, and stop after three rounds per file. The type in the shot is the
   type you named. A fallback face, or text that is missing, means the shot
   was taken before the font arrived: shoot again, and fix the font link if it
   happens twice.
   Done when you have opened every file's latest shot and each reads at a
   glance.

5. **Shoot and report.** After your last commit, write the finished PNG of each
   direction at the exact size into `directions/` (`directions/<n>-<name>.png`),
   and for a print brief its PDF (`directions/<n>-<name>.pdf`), and commit
   them. Confirm each PNG and each PDF mechanically (Exact size, below) and
   open each PNG: it shows the type you named, and one that does not is shot
   again. Attach the three PNGs to one note in the form given in the exact-size
   mechanics. Run `yardr bead done <id>` when the exit in your stage file holds.
   Done when one note carries the three confirmed shots, each opened, and names
   the commit they were shot at.

## House style

A still has one glance to land. These hold in every direction:

- one thing first: the largest element says what the visual is for, and
  everything else is smaller than it. Given five seconds a stranger knows the
  offer and where to act, or feels what the piece is for;
- no more than two typefaces, set big enough to read at the size the visual is
  seen at: a headline that fits its line, body copy in full-strength ink, never
  pale grey on white;
- margins equal on all sides, and nothing the piece cannot lose within a
  twentieth of an edge: crops and thumbs eat edges first;
- colour taken from the subject's own materials, spent where the eye should go;
  ornament without substance reads as unfinished, so spend the space on size (a
  larger headline, a larger figure), on complete content, on artwork drawn for
  this subject, or on a flat field of colour;
- the file is the brief's size exactly: art meant to bleed runs to the file's
  edge. A printer's bleed past trim is beyond this pack; the PDF is the trim
  size.

Tells that fail a direction unless its idea chose them and the note says so: a
single word accented in a headline; labels in all caps; rows of look-alike
cards with an icon above each; gradient blobs and ornamental shapes; grey
placeholder bars where a figure or a picture belongs.

Leave proof out unless the brief gives it: testimonials, client names and
logos, review scores, press mentions and figures. The subject's own content
does that work: its menu, its rooms, its work, its prices. The note says which
details you made up. No lorem ipsum, no Acme Corp, no round vanity totals: a
figure shown carries its unit, and figures shown add up.

## Exact size

Shoot after your last commit, so that the shots and the SHA name the same
state. Each command starts a new shell: keep what a later command needs in
files, and set a variable on the line that uses it.

Chrome is `SHOT_CHROME` where that names an executable, else the first of
`google-chrome`, `google-chrome-stable`, `chromium` and `chromium-browser` on
PATH, else the macOS application
(`/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`).

1. Make a directory outside the worktree for the look-round PNGs (`mktemp -d`),
   so that no shot lands in a commit before its file is finished. The finished
   PNGs and PDFs of step 5 go into `directions/` instead and are committed.
2. Shoot each file from the worktree's root over `file://`, at the brief's
   exact pixels with `--window-size` and `--screenshot`:
   `"$CHROME" --headless --disable-gpu --hide-scrollbars
   --force-device-scale-factor=1 --window-size=<W>,<H>
   --screenshot="$d/<n>-<name>.png"
   "file://$(pwd)/directions/<n>-<name>.svg"`.
   An `.html` direction is shot the same way, by its own file name. Chrome
   prints display errors on a Mac, and the bytes written decide: a run that
   writes nothing failed, so run it again. Chrome gets a minute: a slower shot
   fails with the reason, so run it again.
   For a print brief the pixels are the trim size at 96 CSS pixels to the
   inch: A4 (210x297mm) is 794x1123, A5 (148x210mm) is 559x794, US Letter
   (8.5x11in) is 816x1056. Any other trim size is its inches times 96, stated
   in the plan note.
3. Confirm each PNG mechanically: `file "$d/<n>-<name>.png"` names
   `PNG image data, <W> x <H>`, and the file is not empty (`test -s`). That
   catches a missing file or a wrong size. A blank shot or one in a fallback
   face passes it, so open each PNG as well.
4. For a print brief, print each direction's PDF from HTML: an `.html`
   direction carries `@page` at the trim size with margin 0 and is printed
   itself. An `.svg` direction is printed through a self-contained wrapper
   page in the temp directory. The SVG's width and height are the trim size in
   real units; use those same units for `<trim width>` and `<trim height>` in
   the wrapper. Set `d` to the temp directory, then make the wrapper:
   ```sh
   d=<temp directory>; SVG=directions/<n>-<name>.svg W=<trim width> H=<trim height> OUT="$d/<n>-<name>-print.html" python3 - <<'PY'
   import base64, os
   svg = base64.b64encode(open(os.environ["SVG"], "rb").read()).decode("ascii")
   w, h = os.environ["W"], os.environ["H"]
   html = '''<!doctype html><meta charset="utf-8"><style>
   @page { size: __WIDTH__ __HEIGHT__; margin: 0 }
   html, body { width: __WIDTH__; height: __HEIGHT__; margin: 0; overflow: hidden }
   img { display: block; width: 100%; height: 100% }
   </style><img src="data:image/svg+xml;base64,__SVG__">'''
   html = html.replace("__WIDTH__", w).replace("__HEIGHT__", h)
   html = html.replace("__SVG__", svg)
   open(os.environ["OUT"], "w", encoding="utf-8").write(html)
   PY
   ```
   The wrapper embeds the SVG, so Chrome does not have to load a second local
   file. Print that wrapper, not the SVG or a different direction file:
   `d=<temp directory>; "$CHROME" --headless --disable-gpu
   --print-to-pdf="$d/<n>-<name>.pdf" --print-to-pdf-no-header
   "file://$d/<n>-<name>-print.html"`.
   Confirm each PDF mechanically: `file` names `PDF document` with 1 page, and
   `grep -a -o 'MediaBox \[[^]]*\]'` names the trim size in points: A4 is about
   595x842, A5 about 420x595, US Letter 612x792.
5. Under 500 wide, shoot through a frame: Chrome on macOS lays every narrower
   window out 500 wide and the PNG is its left slice. Write a wrapper page in
   the temp directory with one iframe at the exact size
   (`<iframe src="<file>" width="<W>" height="<H>">`, no border, no margin)
   and shoot the wrapper at `--window-size=<W>,<H>`. The PNG's left edge is
   then the frame's, at the exact width.
6. Attach the finished PNGs to one note, words first, each file with its own
   `--file`:
   `yardr bead note <id> "<words>" --file directions/1-<name>.png --file directions/2-<name>.png --file directions/3-<name>.png`.
   The words say what was opened (the three files under `directions/` on
   `yard/<bead>`), at what size (the exact pixels; for a print brief the pixels
   and the trim size, with its PDF), and the commit's short SHA
   (`git rev-parse --short HEAD`).
   Then the one-line reading of the brief, then a line for each file,
   `<n>. <name>: <idea>; <type pairing>; <palette>`, with its idea line and
   its address on the watch (step 3), then the details you made up. The note's
   file is the record, not the local path.
7. Remove the temp directory as a command of its own, with the variable set on
   the line that deletes: `d=<dir>; rm -rf "${d:?}"`.

## Something outside your bead

1. It does not block you (a bug elsewhere, a stale doc): file it and carry on,
   never fix it on your branch. `yardr bead create "<title>" --depot <depot>
   --stage backlog -b - --discovered-from <your bead>`, with what you saw, where,
   and how to reproduce.
2. It blocks you and is no question about the bead (no headless Chrome, no
   yardr checkout to read): file it as above, then mail {{yardmaster}}:
   `yardr mail {{yardmaster}} "<bead id>: <one line>"`. If you cannot finish
   without it, `yardr bead note <id> "blocked by <bead id>" && yardr bead hold
   <id>`.
3. It is a question about the bead itself, an unclear brief or a choice that is
   the person's: that is the `question` outcome of the stage.

Shots weigh on your context. When each turn slows, write a note of where you are
(done, next, open) and hold the bead with `yardr bead hold <id>`; the next
session starts from the note.
# Setting up the visuals pack

The pack adds the bead type `visual` to a git depot. A brief goes in: what the
visual is and how big. A designer agent composes three directions at that exact
size, shoots them, and puts the shots on a note, and a person picks one and
holds the finished file. Nothing lands. This file is the operator's steps.

The commands call the depot `studio` and the pack's directory (the `visuals`
directory of the yardr-packs checkout) `<pack dir>`. Change both to yours.

The designer's shots need two things on the yard's machine: headless Chrome
(Google Chrome in `/Applications`, `google-chrome` or `chromium` on the PATH, or
a path in `SHOT_CHROME`), run directly at the brief's exact size, and `python3`.
The directions load their typefaces from CDNs, so the machine needs the network
while a designer works.

A designer starts a watch on the directions while it builds
(`yardr preview watch`, in yardr from 4 November 2026), so that a person can
open the files in a browser as they take shape. The designer's notes give the
address of each file: the address the command prints, followed by the file's
name. The command prints the host the yard's edge gave the watch where the yard
is linked to an edge that gives one, and the path `/preview/<app>/` on the yard
otherwise.

1. **The machine.** Each command below is one thing to type, in a block of its
   own. The first finds Chrome. The second prints the first line of the page on
   the watch, and on a yardr that has no watch says `no doc named "watch"`. Its
   designers get no `opens at` line from the command, and put what it printed
   on their notes in place of the addresses:
   ```sh
   command -v google-chrome google-chrome-stable chromium chromium-browser; ls "/Applications/Google Chrome.app" >/dev/null && echo macOS Chrome
   ```
   ```sh
   yardr docs watch | head -1
   ```
2. **What the import would do.** Nothing is written:
   ```sh
   yardr pack check <pack dir> --depot studio
   ```
   It lists the flow `visuals.visual`, the pointer that makes it the flow of
   type `visual`, the routes of its stages, the group `studio-visual-designers`
   and the questions the next step asks. A depot that follows the webdesign
   pack's mockup flow keeps it: that flow is for the type `mockup`, and this
   one is for `visual`. Read the `crew` lines too: the `yardmaster` itself is
   unchanged, and the plan creates its two subscriptions, a bead entering
   `pick` and a bead closing.
3. **Import it.** The import asks which agent runs `studio-visual-designers`:
   its kind, its model and its permission arguments. A designer runs shell
   commands (git, Chrome, yardr) and opens PNGs to look at its own work, so
   pick a kind and model that read images, and the permission arguments your
   builders have. With none, it stops at every permission prompt.
   ```sh
   yardr pack apply <pack dir> --depot studio
   ```
   To answer on the command line instead of at the prompts (the kind and model
   are an example):
   ```sh
   yardr pack apply <pack dir> --depot studio --yes --bind visual-designers=claude:opus
   ```
   The groups `backlog` and `decisions` are the yard's two people groups, and the
   import reuses a yard's own. They hold the people who ready a brief and who
   pick. A yard without them is asked for their members, and `--members
   backlog= --members decisions=mckean` answers (empty means anyone). A yard
   whose crew member is not called `yardmaster` says which one takes it, with
   `--crew yardmaster=<name>`. An agent group the yard already has can take the
   designers' place, with `--group visual-designers=<group>`: its own prompt
   stays, and the pack's role is installed and read by nobody.
4. **Make a bead.** The brief is the bead's body: the format, the size (exact
   pixels for screen, the trim size for print), the subject, the audience, the
   job, the words, what the brief fixes, reference work and what is not wanted.
   The stage file of `backlog` lists them and is shown to whoever opens the
   bead. The type `visual` follows this pack's flow, which starts in
   `backlog`:
   ```sh
   yardr bead create "Three directions for the fete poster" --depot studio --type visual -b "<the brief>"
   ```
   `-b -` reads the body from standard input instead. When the brief is ready,
   send the bead to the designers:
   ```sh
   yardr bead advance <id> --to directions
   ```
5. **Watch it built.** Once the designer has written its first file it starts
   the watch and puts the three addresses on a note, which appears on the bead
   as any note does:
   ```sh
   yardr prime --bead <id>
   ```
   Open the addresses in a browser; one that starts with a slash is a path on the
   yard's web view, so put the view's address in front of it. A file reloads
   when it changes, and a file the designer has not built yet answers 404 until
   it has. The Previews page of the web view lists the watch as well, but its
   Open button opens the folder, which answers 404: the files are at the
   addresses in the note. The watch ends when the bead leaves `directions`, and
   the shots on the last note are the record.
6. **The pick.** When the designer is done the bead waits in `pick`, and the yard
   puts a card on it that offers the moves. The person reads the shots on the
   designer's note (`yardr prime --bead <id>` prints it, and the web view shows
   the images), then picks. The same move by hand, with the pick in words:
   ```sh
   yardr bead advance <id> --to picked --note "2, with the type of 3"
   ```
   The other two moves are `--to directions`, for another round with a note that
   says what to change, and `--to backlog`, to park the bead or reshape its
   brief.
7. **After the pick.** The bead is closed at `picked`. The directions stay on the
   branch `yard/<id>`, under `directions/`, and the shots on the note. Nothing
   lands on the base. What you hold is the picked file with its PNG at the
   exact size (and its PDF where the brief is for print), on the branch.

One designer works at a time, because each runs headless Chrome. To allow more,
raise the limit of the group the import made:

```sh
yardr group set studio-visual-designers --limit 2
```
#!/bin/sh
# The visuals pack's tests: what the gate runs beside yardr pack check.
# Needs no network, no secret and no yardr, and is done in well under a second.
# The pack is text, so there is nothing to run. What the test pins is what a
# later edit could lose without a sound:
#
#   role      roles/visual-designer.md carries the distinctness rule, the
#             exact-size mechanics (Chrome run directly, the PNG shot and its
#             check, the print PDF wrapper and its check, the frame under 500
#             wide)
#             and the watch (the command with the bead's id, how a file's
#             address is made, and the two notes that carry the addresses)
#   stages    each stage file has its exit line, directions.md names the
#             finished PNG and PDF, the role's distinctness rule and the
#             address of each file on the watch, and pick.md has the card of
#             its moves
#   shape     the decisions of pack.yaml: the pack's own type and group, no
#             assembly, the yardmaster hearing pick and carrying no role
#   closed    no role or stage file points at a file the import does not install
set -u
cd "$(dirname "$0")" || exit 1
fail=0

# ok <name> <condition's exit status> [detail]
ok() {
  if [ "$2" = 0 ]; then echo "test: $1 ok"; else echo "test: $1 FAILED${3:+: $3}"; fail=1; fi
}
# has <file> <fixed text>: the text is in the file.
has() { grep -qF -- "$2" "$1"; }

role=roles/visual-designer.md

# ---- the role ---------------------------------------------------------------
# What the stage files name, so it stays in the role: the distinctness rule and
# the exact-size mechanics. And what the mechanics teach, so that an edit cannot
# drop a step without a sound: Chrome run directly with its window and shot
# flags, what `file` names for the PNG, the print PDF with its page rule and
# its MediaBox check, and the frame under 500 wide. And the watch the designer
# starts for the person: the command with the bead's id, how a file's address
# is made from what the command prints, what to do when it prints none (a yardr
# without the watch ends 0 on it), the note that carries the addresses while
# the designer builds, and the line of the shots note that carries them after.
for t in 'The distinctness rule' '--window-size=<W>,<H>' '--screenshot=' \
         'PNG image data, <W> x <H>' '--print-to-pdf=' '@page' 'MediaBox' \
         'data:image/svg+xml;base64,' 'file://$d/<n>-<name>-print.html' \
         'Print that wrapper, not the SVG or a different direction file' \
         'Under 500 wide' 'directions/<n>-<name>.svg' '--file' \
         'yardr preview watch $YARDR_BEAD directions' 'followed by its file name' \
         'If the command prints no `opens at` line' \
         'Put the three addresses on a note at once' 'its address on the watch (step 3)'; do
  has "$role" "$t"
  ok "role: carries '$t'" $?
done

# ---- the stage files --------------------------------------------------------
# The exit line is the lead-in of each stage's list of what must hold.
for row in 'backlog|Before it is sent to `directions`' \
           'directions|Before the bead leaves as `done`:' \
           'pick|A bead leaves `pick` when'; do
  f=flows/visual/${row%%|*}.md
  line=${row#*|}
  has "$f" "$line"
  ok "$f: its exit line '$line'" $?
  head -1 "$f" | grep -q '^summary: .'
  ok "$f: opens with a summary line" $?
done
grep -q '^## Ask$' flows/visual/pick.md && grep -q '^kind: moves$' flows/visual/pick.md
ok "flows/visual/pick.md: ends in the card of its moves (kind: moves)" $?
has flows/visual/directions.md 'your role'"'"'s distinctness rule'
ok "flows/visual/directions.md: names the role's distinctness rule" $?
has flows/visual/directions.md 'directions/<n>-<name>.png'
ok "flows/visual/directions.md: its exit names the finished PNG at the exact size" $?
has flows/visual/directions.md 'directions/<n>-<name>.pdf'
ok "flows/visual/directions.md: its exit names the PDF of a print brief" $?
has flows/visual/directions.md 'address on the watch'
ok "flows/visual/directions.md: its exit names the address of each file on the watch" $?
has flows/visual/backlog.md 'the size:'
ok "flows/visual/backlog.md: the brief names the size" $?

# ---- pack.yaml --------------------------------------------------------------
yaml=$(grep -v '^ *#' pack.yaml)
y() { printf '%s\n' "$yaml" | grep -q "$@"; }
y '^    type: visual$'
ok "pack.yaml: the flow is for the pack's own type visual" $?
y 'directions: { group: visual-designers, file: flows/visual/directions.md, outcomes: { done: pick, question: pick }, manual: \[backlog\] }'
ok "pack.yaml: directions is worked by visual-designers, and done and question both go to pick" $?
y 'pick:       { group: decisions,        file: flows/visual/pick.md,       manual: \[picked, directions, backlog\] }'
ok "pack.yaml: pick moves by hand to picked, directions or backlog" $?
y 'picked:     { end: true }'
ok "pack.yaml: the flow ends at picked" $?
y 'visual-designers: { agent: roles/visual-designer.md, limit: 1 }'
ok "pack.yaml: the visual designers are a group of their own, one at a time" $?
! y 'builders'
ok "pack.yaml: no group of the depot's builders" $?
! y ' designers[^_]'
ok "pack.yaml: no group of webdesign's designers" $?
! y 'run:'
ok "pack.yaml: no script group, so nothing lands" $?
y 'stages: \[pick\]'
ok "pack.yaml: the yardmaster hears a bead enter pick" $?
! y '^    role:'
ok "pack.yaml: the yardmaster carries no role" $?

# ---- closed -----------------------------------------------------------------
# An import installs the role, the stage files and the setup notes: a role or a
# stage file that sends its reader to another file of the pack sends it nowhere.
! grep -n 'THIRD_PARTY_NOTICES\|README\|SETUP\.md' "$role" flows/visual/*.md
ok "role and stage files: point at no file of the pack that the import leaves behind" $?

if [ "$fail" = 0 ]; then echo "test: visuals: all ok"; else echo "test: visuals: FAILED"; fi
exit $fail