@ethlete/agent-rules 0.1.0-next.26 → 0.1.0-next.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md ADDED
@@ -0,0 +1,28 @@
1
+ # agent-rules
2
+
3
+ `libs/agent-rules` publishes `@ethlete/agent-rules`: the portable slice of this repo's
4
+ guidance, compiled into Claude Code, Codex, Cursor and Copilot formats. Content lives in
5
+ `libs/agent-rules/content/`; what this repo itself consumes is configured in
6
+ `ethlete-agents.config.json` (`profile: sdk`, so only `scope: both` content is emitted
7
+ here). Never hand-edit the generated marker block at the end of the root `AGENTS.md` or a generated
8
+ `ethlete-*` skill - edit the content file in `libs/agent-rules/content/`, then:
9
+
10
+ ```bash
11
+ npx prettier --write libs/agent-rules/content/<file> # format first
12
+ yarn agents:sync # then regenerate
13
+ ```
14
+
15
+ A few content files are vendored from another repository. Keep them as written upstream,
16
+ apart from the generator's link syntax - `libs/agent-rules/THIRD-PARTY-LICENSES.md` names
17
+ each one, its upstream commit and its license.
18
+
19
+ `content/output-styles/` is the exception: a Claude Code output style is a machine-level
20
+ file, so `sync` never emits it and `agents:check` never diffs it. It is installed on
21
+ demand instead - `node dist/libs/agent-rules/src/index.js output-style` after a build here,
22
+ `npx ethlete-agents output-style` in a consumer repo.
23
+
24
+ Format **before** syncing. The generated copies under `.claude/` and `.agents/` are
25
+ Prettier-ignored, so formatting the content afterwards silently leaves them stale, and
26
+ `yarn agents:check` (which CI runs) fails. The `npx ethlete-agents sync` in the published
27
+ README works in consumer repos only - here the package is the source, not a dependency,
28
+ so `npx` would look it up on the registry and 404.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,96 @@
1
1
  # @ethlete/agent-rules
2
2
 
3
+ ## 0.1.0-next.27
4
+
5
+ ### Minor Changes
6
+
7
+ - The design-exploration skill says every `et design` write takes `--by <model id>`, that `sketch report` names its variant with `--variant`, and that the coordinator writes the exact model id into each sub-agent prompt; an agent never guesses it.
8
+ - The design-exploration skill says a flow may name a page that exists only as an open sketch, and Studio draws its first variant.
9
+ - The design-exploration skill says elements may draw with the wireframe kit, and a rough shell is a kit element each page imports.
10
+ - The design render check warns of grid rows that leave out interaction columns other rows fill (`GRID-COVERAGE`).
11
+ - `design check` warns of a `.replace` on markup that neither the part nor a part it uses draws (`STALE-REPLACE`).
12
+ - The design render check warns of a class with a product prefix that no stylesheet of the frame defines (`UNDEFINED-CLASS`).
13
+
14
+ ### Patch Changes
15
+
16
+ - The design-exploration skill explains the open, addressed and resolved comment states, and that the agent never merges after a pick.
17
+ - The design-exploration skill says when to pass `--question` to `et design comment reply`.
18
+ - The design-exploration skill covers `et design comment reply` and the inbox group "waiting for the user".
19
+ - The design-exploration skill says to call `node dist/libs/cli/src/index.js design` in a check-and-shot loop instead of `yarn design`, which builds the CLI first.
20
+ - The design-exploration skill now forbids `vh`, `dvh` and `svh` in parts, and requires every flow line to carry a label naming an element visible in the frame before it.
21
+ - Document product flows, the wireframe kit and the FLOW and WIREFRAME-KIT checks in the design-exploration skill.
22
+ - The design exploration skill explains comments on an element variant inside a page.
23
+ - The design-exploration skill says a product's own `presets` replace its base's, so a desktop-only app lists `desktop` alone.
24
+ - Tell design agents that a Tailwind product registers its drawing folders in a stylesheet, so sketches use the app's utilities.
25
+ - Explain a sketch's `shared/` folder, `sketch new --like` and `embedPage()` in the design-exploration skill.
26
+ - Teach the design-exploration skill the product and sketch workflow of Ethlete Studio: `et design product new`, sketches, the inbox, and Studio-side merge.
27
+ - The design exploration skill describes the product config `tokens` list.
28
+ - The design-exploration skill says how a part imports the product's tokens through `@system/foundations/tokens.css?inline`.
29
+ - The design and git-commit skills add rules for variants, pages and commits in a shared checkout.
30
+ - The design-exploration skill draws wireframes first, then foundations, logo and CI, components, elements and pages.
31
+ - The design-exploration skill says a part with `definePart({ styles })` may draw `drawing({ body })` without styles.
32
+ - The design-exploration skill says `et design inbox done` needs `--note`, which replaces the part's note.
33
+ - The design-exploration skill says a part may export named helpers, and that another part imports a piece instead of copying its markup.
34
+ - The design-exploration skill says `sketch check` and `sketch ready` note a problem the product already had instead of failing on it.
35
+ - The design-exploration skill says `sketch new` starts no report, so `report --done` follows only a report of your own.
36
+ - The design-exploration skill says to start each `et design sketch report` message with `variant <name>`, so Studio can mark that variant as drawing.
37
+ - The design-exploration skill names `sketch round` and says `sketch list` shows the rounds.
38
+ - The design-exploration skill tells agents to answer a combine comment with `et design sketch variant --from` in a new round, noting why the old round lost.
39
+ - The design-exploration skill makes wireframes strict: keep what the app already has, one sketch per screen, regions and names only, no polish, no subagent, and stop the stage early.
40
+ - The design-exploration skill lists the wireframe kit's new controls (`select`, `tabs`, `segmented`, `checkbox`, `badge`, `dot`), an icon `glyph` and a box `fill`, and says to draw a control as its type.
41
+ - The design-exploration skill teaches the wireframe kit's `heading`, `image` `ratio` and row `stackBelow` instead of fixed sizes.
42
+ - The delegation rule says a subagent starts with the full start context, so a small lookup stays in the session. The format rule asks for one Prettier call over all edited files.
43
+ - The design-exploration skill says the auto merge blocks only on problems it adds, and that `sketch ready` can be re-run on a ready sketch.
44
+ - The design-exploration skill says that `design check` warns of a merged part changed outside a sketch.
45
+ - Design parts and stylesheets load product assets as `@assets/<file>`.
46
+ - `et design check --tsconfig` also typechecks each variant of an open sketch and prints the tsc output of a failing one.
47
+ - `et design sketch new` takes `--note <text>`, the note of the first round.
48
+ - `et design <command> [<subcommand>] --help` prints the usage of that one command and skips its checks.
49
+ - `et design check` reports a `var(--token)` without a fallback that no part or stylesheet of the product or its base defines.
50
+ - `et design shot --crop x,y,width,height` and `/__design/shot?clip=x,y,width,height` cut a picture to a box in CSS pixels, so a hairline can be judged with `--scale`.
51
+ - `et design shot --variants --diff-props` compares each variant with the merged part drawn with the variant's state props, so changed props no longer paint the whole diff red.
52
+ - `et design shot --interaction <hover|focus-visible|active|disabled>` forces an interaction on every cell, and `--help` names it. The design-exploration skill names it.
53
+ - The diff row of `et design shot --variants` says "no pixel change, but the markup differs" when a variant draws other markup to the same pixels.
54
+ - `et design shot --scale <1-4>` and `/__design/shot?scale=` draw a part at a device scale, so a small part is legible. The design-exploration skill names it.
55
+ - `et design shot --state <a>,<b>` draws only the named states, side by side, and names the states of the part when one does not exist. The design-exploration skill names it.
56
+ - `et design sketch check --fix` writes the variants in the checkout's Prettier format before it checks them; a FORMAT problem points to it.
57
+ - `et design sketch check` warns of a variant that restyles a class of the sketch's `shared/` folder and of a variant file nothing imports.
58
+ - `et design sketch new --round` requires `--note`, so `sketch ready` no longer stops on a round without a note.
59
+ - `et design sketch new` takes `--variant-note`, `sketch variant` takes several variants, and sketch commands format their output, explain each flag and infer `--product`.
60
+ - `et design sketch variant` refuses a sketch the user discarded and asks for a variant pick first; a pick is still cleared.
61
+ - `design check` warns of an export in a part's `shared/` that nothing imports, and `sketch check` of a sketch `shared/` export a variant does not import.
62
+ - `sketch new --variant-note` keeps each variant's note in `sketch.json` instead of overwriting the part's merged note; Studio shows it in the variant's head, and a merge gives it to a new part.
63
+ - A sketch variant may hold its own copy of a shared file at `<variant>/shared/<path>`: `@shared/<path>` resolves to it first in rendering, typechecks and checks, and a merge of that variant carries it.
64
+ - The context-warning hook now tells the agent to apply the handoff save test before it suggests a handoff. When nothing is left to hand off, the agent suggests a fresh session instead.
65
+ - The design-exploration skill says when to run `design accept`.
66
+ - `et design check` also type-checks each variant of an open sketch and fails with a `TYPE-ERROR` line per broken variant. A merge's own check still types only the product.
67
+ - The design-exploration skill lists the render rules of `design check` and how a part opts out of one.
68
+ - The design exploration skill documents component states per axis, and treats an icon set as a foundation asset.
69
+ - The design exploration skill extracts a component only when two hosts draw the same case.
70
+ - `forceInteraction` from `@design-system` forces an interaction on one element of a part's markup, not only its root.
71
+ - The design-exploration skill says the frame loads a product's tokens, so parts stop importing them.
72
+ - The render check reads each drawn part's `grid`: `sketch check` prints `grid <rows> x <columns> ok` or the states it leaves out, and a state in two cells or a cell naming no state fails as `GRID`.
73
+ - `interactionRows` from `@design-system` builds a part's states and grid from one props object per row and a list of interactions.
74
+ - `et design part remove <layer>/<name>` deletes a merged part with its `merged.json` entry and tsconfig path, and resolves its comments. It refuses a part another part imports, a base part, and a part with an open sketch.
75
+ - `definePart({ styles })` exposes a part's stylesheet on its default export and adds it to every drawing of the part.
76
+ - Tell the design-exploration skill to take its own check shots with `et design shot` instead of a scratch script.
77
+ - The design exploration skill now sends every change to a merged part through a sketch. When there is nothing to choose, the sketch has one variant.
78
+ - The design-exploration skill keeps a component's frame-only wrapper in `render` and out of the markup a host embeds.
79
+ - The design-exploration skill mentions the duplicate-class warning of `sketch check`.
80
+ - `svgAsset` sets a `class` and `attrs` on the root `<svg>` of an asset.
81
+ - Add `svgAsset(raw, { size })` to `@design-system`, fail `design check` on an SVG asset without a root `<svg>` or the SVG namespace, print each variant's `assets/` folder, and add a pixel diff row to `design shot --variants`.
82
+ - The design render check reports `UNKNOWN-THEME` when a drawing uses a colour theme that no stylesheet of its frame defines.
83
+ - The grilling skill now asks for questions that read alone: plain words, no chains of question or ADR numbers, a short reminder when a new topic starts, facts checked before a recommendation, and a confirmed summary at the end.
84
+ - When the handoff save test finds nothing to hand off, the handoff skill and the context-warning hook now ask the agent for a prompt the user can copy into the fresh session.
85
+ - The design-exploration skill says the merge note is optional and which sketch commands record a variant's authors.
86
+ - Document design notes in the design-exploration skill: the note file format, the size a pointed note needs, free flow notes, and when to write a note instead of a comment.
87
+ - The design exploration skill says a closed comment thread is resolved, not deleted.
88
+ - The design exploration skill says a merge, discard or new round closes the sketch's open comments.
89
+ - `et design sketch check` and `sketch ready` fail when a variant drops a state the product's part has.
90
+ - `et design sketch ready` merges a sketch by itself when no state of any variant differs by a pixel from the product, and logs it as `auto` in `accepted.json`.
91
+ - `sketch report --variant <key> --done` ends the live "drawing" report of that variant, so Studio shows its frames before `sketch ready`. The design exploration skill names the command.
92
+ - The design-exploration skill says that `sketch variant` without `--round` fails while the sketch has open comments.
93
+
3
94
  ## 0.1.0-next.26
4
95
 
5
96
  ### Patch Changes
package/CLAUDE.md ADDED
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -9,7 +9,7 @@ Commit `3cca18b` (2026-09-04). Vendored skills, kept as written upstream apart f
9
9
  generator's own link syntax:
10
10
 
11
11
  - `content/skills/grill-with-docs` - the entry point; calls the other two.
12
- - `content/skills/grilling`
12
+ - `content/skills/grilling` - plus our own section "Local addition" after the upstream footer.
13
13
  - `content/skills/domain-modeling`
14
14
 
15
15
  MIT License
@@ -176,8 +176,16 @@ AGENT_PROFILES = {
176
176
  "emits_system_message": True,
177
177
  "act_now": "Run /{handoff} now and continue in a fresh session.",
178
178
  "act_later": "consider /{handoff} to continue in a fresh session",
179
- "recommend": "recommend the user run /{handoff} to save state and start a fresh session",
180
- "suggest": "suggest the user run /{handoff} to save state and start a fresh session",
179
+ "recommend": (
180
+ "first apply the handoff skill's save test yourself; if it calls for a file, "
181
+ "recommend the user run /{handoff} to save state and start a fresh session, "
182
+ "otherwise recommend /clear and say nothing is left to hand off, with a prompt to copy into the fresh session"
183
+ ),
184
+ "suggest": (
185
+ "first apply the handoff skill's save test yourself; if it calls for a file, "
186
+ "suggest the user run /{handoff} to save state and start a fresh session, "
187
+ "otherwise suggest /clear and say nothing is left to hand off, with a prompt to copy into the fresh session"
188
+ ),
181
189
  "save_now": (
182
190
  "run the handoff skill's save mode right now - finish only an in-flight atomic "
183
191
  "edit first, nothing new. Apply its own test: with every change committed, no "
@@ -209,11 +217,13 @@ AGENT_PROFILES = {
209
217
  "recommend": (
210
218
  "tell the user you are near the context limit, then follow "
211
219
  ".agents/skills/{handoff}/SKILL.md to save a handoff and have them start a "
220
+ "fresh session; when its save test calls for no file, only tell them to start a "
212
221
  "fresh session"
213
222
  ),
214
223
  "suggest": (
215
- "suggest saving a handoff via .agents/skills/{handoff}/SKILL.md and "
216
- "continuing in a fresh session"
224
+ "first apply the save test in .agents/skills/{handoff}/SKILL.md yourself; if it "
225
+ "calls for a file, suggest saving a handoff and continuing in a fresh session, "
226
+ "otherwise suggest a fresh session and say nothing is left to hand off, with a prompt to copy into the fresh session"
217
227
  ),
218
228
  "save_now": (
219
229
  "follow .agents/skills/{handoff}/SKILL.md and save a handoff right now - finish "
@@ -19,7 +19,8 @@ so let them do the work before correcting anything by hand:
19
19
  For judgment calls lint cannot enforce, load the repository's focused guidance for the
20
20
  code you are changing.
21
21
 
22
- Format every edited Prettier-supported source file before wrapping up. Do not send binary,
22
+ Format every edited Prettier-supported source file before wrapping up, in one call over all of
23
+ them, not one call per file. Do not send binary,
23
24
  generated, or unsupported files to this command:
24
25
 
25
26
  ```bash
@@ -23,6 +23,10 @@ means, and say "keep it brief" for a lookup. Two calls need no `model` of their
23
23
  agent type carries the model and reasoning effort its own definition sets, and a fork always
24
24
  inherits the parent's model.
25
25
 
26
+ A subagent starts cold: it loads the full start context (about 50k tokens) before its first step,
27
+ and measured subagents cost as much as the sessions that spawn them. Do a lookup of a few tool
28
+ calls yourself. Give each subagent one large slice, not several small ones.
29
+
26
30
  A subagent has the same context budget as the session, measured on its own transcript, and the
27
31
  context-warning hook tells it to hand back at the critical tier. Size each prompt to fit: one
28
32
  slice of work, the files it needs named, and "keep your context small - no whole big files".
@@ -11,15 +11,19 @@ vars: [storybookUrl]
11
11
  A design exploration is a dialog. **The user is the designer. You find and frame. The user
12
12
  chooses.** A finding is not a licence to pick the fix.
13
13
 
14
- The unit of work is a **call**: one open question, with every answer drawn side by side at
15
- the same geometry. A variant wins or loses only against the other variants of its call.
14
+ The unit of work is one open question, with every answer drawn side by side at the same
15
+ geometry: a **sketch** on a product, or a **call** in a repository with the old setup. A variant wins
16
+ or loses only against the other variants of its question.
16
17
 
17
18
  ## The rules
18
19
 
19
20
  1. **One open call at a time.** Take one question. Draw its variants. Stop.
20
21
  2. **Put the variants in the call, then name your pick.** Two to four variants, drawn for
21
- real, side by side, labelled, with what each one costs. Your pick is a proposal.
22
- 3. **Commit only when the user says commit.**
22
+ real, side by side, labelled, with what each one costs. Your pick is a proposal. Never ask
23
+ a yes/no question about content, and never ask the user what they want: deliver strong
24
+ variants, and the user picks.
25
+ 3. **Commit only when the user says commit.** A merge the user makes in Ethlete Studio counts as
26
+ that word for the merged part: commit the part, not the open sketches.
23
27
  4. **A clear rejection starts the next call.** Settle and record the rejected call, then draw
24
28
  the next focused question automatically, within the user's original scope. Stop only when the
25
29
  user asks to pause or end the exploration, or when their feedback leaves no grounded next
@@ -32,13 +36,17 @@ finished while the user is still talking about it.
32
36
  ## What one stop looks like
33
37
 
34
38
  - The question, in one sentence.
35
- - The call slug to look at.
39
+ - The sketch (or call slug) to look at.
36
40
  - The variants, labelled A, B, C, one line each.
37
41
  - Your pick, one line on why.
38
42
  - Nothing else. No next step, no second finding.
39
43
 
40
44
  **Do not send a screenshot.** The user keeps the call open. Screenshot only for your own
41
- check that a variant renders.
45
+ check that a variant renders. In a repository with `et design`, take it with
46
+ `et design shot <layer>/<name> --states [--variants]`: one contact sheet of every state, and
47
+ every variant of the open sketch, with a label per cell; below each variant row, a diff row marks
48
+ the pixels that differ from the merged state in red, or says "no pixel change" (with ", but the markup differs" when only the markup changed, e.g. both sides miss the same content). It prints the PNG path last. `--scale 2` enlarges a small part; `--crop x,y,w,h` cuts each cell to that CSS-pixel box, to judge hairlines. `--interaction hover` forces an interaction on every cell. `--state a,b` draws only those states, to compare them side by side. With `--variants`, `--diff-props` compares each variant with the merged part drawn with the variant's state props, so changed props do not paint the diff red. It draws
49
+ undimmed, whatever Studio shows. Do not write a scratch script for batch shots.
42
50
 
43
51
  If a second defect appears, add it to the open list and give it one line. Do not draw it.
44
52
 
@@ -46,108 +54,373 @@ If a second defect appears, add it to the open list and give it one line. Do not
46
54
 
47
55
  The exploration's plan file names the tool. Two shapes exist.
48
56
 
49
- **A repository with a `.ethlete/design` folder** draws each variant of a call in its own
50
- frame, and the user reads the call in Ethlete Studio. The tool is `et design`, from `@ethlete/cli`, so the repository itself needs no design
51
- tooling. A call argues in the repository it is about:
57
+ **A repository with a `.ethlete/design` folder** keeps one design system per **product**, and the
58
+ user reads and decides in Ethlete Studio. The tool is `et design`, from `@ethlete/cli`; run
59
+ `et design --help` for every command, `et design <command> [<subcommand>] --help` for one.
60
+ In the SDK checkout, `yarn design` builds the CLI before each call; in a check-and-shot loop,
61
+ build once and call `node dist/libs/cli/src/index.js design` instead.
52
62
 
53
63
  ```
54
64
  .ethlete/design/
55
- config.json the port, the default call, and one entry per project
56
- calls/<project>/<group>/<slug>/
57
- call.ts the eyebrow, the headline, the intro, frameWidth, the variants and the round prose
58
- fixture.ts the data every variant shares
59
- variant-a.ts one default-exported drawing per variant
60
- variant-b.ts
65
+ config.json the port and one `products` entry each
66
+ <product>/<layer>/<part>/ layer: foundations, components, elements, pages
67
+ <product>/flows/<name>.json a flow: pages and their states joined by lines
68
+ <product>/sketches/<layer>-<name>/ an open sketch: sketch.json and one folder per variant
69
+ <product>/comments/<id>.json a comment the user wrote in Studio
70
+ calls/ old calls: Legacy, read-only
61
71
  ```
62
72
 
63
- The first segment of a slug is the **project**. `config.json` gives each project its own
64
- `styles` and its own `head`, so one project's type scale and fonts never reach another's
65
- drawings. A project the config names not draws bare.
73
+ Studio no longer takes new calls. **New work goes to a product, never to a new call**; do not run
74
+ `et design new`. Old calls stay in Studio's Legacy tab as reference for moving them into a
75
+ product. They take no picks and no new variants.
66
76
 
67
- A project can also name `themes`: `{ "surface": ["light", "dark"], "color": ["brand", …] }`; the
68
- first name of each list is the default. A frame asked for a theme gets the
69
- `et-surface--<name>` and `et-color--<name>` classes on its root, so a drawing in such a project
70
- must not set a surface or colour class on its own root, and must take its colours from the theme
71
- tokens. An inner panel may set `et-surface--<name>`, to put a dark and a light panel side by side
72
- or to draw an elevated card. To try another value of a colour theme, a variant calls `themeCss()`
73
- in its own `styles`; it never changes the project's providers.
77
+ ### Create a product
74
78
 
75
- ### Make and track a call
79
+ ```bash
80
+ et design product new <name> [--base @ethlete/components|<path>] [--from-project <name>]
81
+ ```
82
+
83
+ It writes the `config.json` entry and the empty layer folders. A product on `@ethlete/components`
84
+ takes the SDK as its base, so its parts start from the real components rendered with `ng()`.
85
+ Without a base it gets the presets phone (390x844) and desktop (1440x900). A product's own `presets`
86
+ replace its base's whole: a desktop-only app lists `desktop` alone and draws no phone state. `--from-project` copies
87
+ `styles`, `head`, `providers` and `themes` from an old project entry. Studio never writes
88
+ `config.json`; the command does.
89
+
90
+ Every frame loads the product's tokens stylesheets once, before the part's own `styles`, so a part
91
+ never imports the tokens and never adds them to its `styles`. The product's config `tokens` lists
92
+ those parts in load order (`["foundations/tokens"]` when left out); a listed part that does not
93
+ exist yet is skipped. A product's own copy of a listed part, or a sketch of it, replaces the base's
94
+ in every frame. Never list a `tokens.css` in config `styles`.
95
+
96
+ A part file default-exports its `definePart(...)` and may also export named helpers, such as a piece of its
97
+ markup: `import { studioMark } from '@system/foundations/logo'`. Import a piece from the part that draws it;
98
+ never copy its markup into another part. A piece that uses the part's classes needs its stylesheet too
99
+ (`@system/foundations/logo.css?inline`), or from the part's default export when the part sets
100
+ `definePart({ styles })`: every drawing of that part gets it, so `render` returns only extra styles, or `drawing({ body })` with none. The layer
101
+ rules hold for these imports as well.
102
+
103
+ A component a host embeds exports its markup as such a helper, and a wrapper only its own frame needs, such as
104
+ padding that makes room for a focus ring, goes in `render` alone, never in that helper: the host places the
105
+ component itself and would inherit the wrapper's offset.
76
106
 
77
- `et design --help` lists every command. Start a call with the tool, never by hand:
107
+ A product whose app uses Tailwind lists one stylesheet in config `styles` that imports the app's styles and
108
+ registers the drawing folders with `@source './'`. Sketches then use the app's utilities; plain CSS stays for what
109
+ utilities cannot express, such as camera geometry.
110
+
111
+ ### Propose a change as a sketch
112
+
113
+ A change to a part, or a new part, is a **sketch**: the question is the part, the variants are the
114
+ answers. Variants are drawn at the same geometry, and one variant wins only against the others.
78
115
 
79
116
  ```bash
80
- et design new <project>/<group>/<name> --stage component --variants a,b --feature <name>
81
- et design status <slug> drawing # while you draw; check sets checking, then ready or failed
117
+ et design sketch new <layer>/<name> --variants a,b --by <model id> [--like <layer>/<name>] [--variant-note a=<text>]… [--product <p>] # each variant starts as a copy of the part
118
+ et design sketch report <layer>/<name> --variant b --by <model id> "phone" # tell Studio what you build now
119
+ et design sketch report <layer>/<name> --variant b --by <model id> --done # you stopped drawing b; Studio shows its frames
120
+ et design sketch check [<layer>/<name>] [--fix] # each variant laid over the product
121
+ et design sketch ready <layer>/<name> --by <model id> # now the user can pick
82
122
  ```
83
123
 
84
- `new` writes `call.ts`, `fixture.ts` and one variant file per key, and the call renders at
85
- once. `stage` in `call.ts` names the stage of the design (see "The order of a design"). Every
86
- call needs one: `new` refuses to start without `--stage`, and `et design check --call` fails on a
87
- call without it. Ethlete Studio groups the calls by it, and the check warns when an element, page
88
- or prototype call imports nothing from the design system. The status shows on the call's row in Studio.
89
-
90
- `feature: '<name>'` in `call.ts` groups a call under a feature in Studio. Set it at creation with
91
- `new --feature <name>`. A call without one shows under "No feature".
92
-
93
- `et design [checkout]` serves the frames on the port `config.json` names. The
94
- checkout defaults to the working directory, and `DE_PORT` overrules the config, so two
95
- checkouts can be drawn at once. A repository that builds the tool rather than installing it
96
- wraps this in a script - in the ethlete SDK, `yarn design:check`. There, the user reads the
97
- calls in Ethlete Studio, started with `yarn studio`, and Studio serves the frames itself. Do not
98
- start `yarn design` next to it.
99
-
100
- Ethlete Studio carries its own copy of the tool, so it draws a checkout that installs nothing.
101
- The machine still needs Node, and the render stage still needs `playwright` where the copy in
102
- use can reach it.
103
-
104
- `call.ts` calls `defineCall` from `@design-explore`. Each variant carries a `key`, a `name`,
105
- a `claim`, a `cost`, an optional `verdict` of `chosen` or `rejected`, and a `load` that
106
- imports its own module.
107
-
108
- In a call that runs past about a dozen variants, every variant names the pass that drew it with
109
- `round: 'r3'`. **That tag is the only thing that makes a round exist**, in the order the variants introduce
110
- them. A round whose variants all carry a verdict is **settled**.
111
-
112
- `rounds` is prose only. Each entry gives a `key`, a `title` and a `note` saying what came out
113
- of that pass, which is how the intro stays short and each pass reads as a reply to the one
114
- before. An untagged round still draws - it says the bare key until somebody writes the entry. A
115
- `rounds` entry that no variant names is stale prose, and the check reports it. Write the note in the same pass that sets the verdicts, never
116
- before the user rules.
117
-
118
- A call with one variant and no `claim` is a **view**: one reference picture, drawn full
119
- width with no verdict tag. Use it for a picture that answers no question.
120
-
121
- Two call-level fields take a call out of the open list:
122
-
123
- - `closed: 'shipped'` once the app implements the call, `closed: 'superseded'` once another
124
- call replaced it. A closed call counts as settled, even with unruled variants.
125
- - `waitsFor: '<slug>'`, the full slug of another call, when this call is parked on it. Studio lists
126
- it under "On hold" and leaves it out of the open counts until that call settles. Then it is
127
- open again by itself, so remove the field only when the reason changes.
128
-
129
- Three constraints the tool puts on a variant file:
130
-
131
- - **Never import a package barrel.** The libraries resolve to source, so one barrel makes
132
- the browser request every module in the library, and the requests fail with
133
- `ERR_INSUFFICIENT_RESOURCES`. Import the one file you need.
134
- - **The fixture is shared, and a variant may not change it.** Variants drawn at three
135
- geometries cannot be compared.
136
- - **One file per variant**, so a broken variant breaks its own frame only.
124
+ Variants are alternatives. A variant never stands for a state of another variant: a hidden or
125
+ collapsed mode of variant d is a further state of d, not a variant e. Before you draw a new control,
126
+ look for a control that already does the job (an active rail button that toggles its panel), and
127
+ draw that first.
128
+
129
+ A section of an app is a page under `pages/`, never a state of the shell. An element holds its
130
+ own chrome only. A sketch on an element adds no state whose difference is page content in a slot.
131
+
132
+ A variant mirrors the product layout and holds only the parts it changes. Draw two to four
133
+ variants, each with what it costs, and run `sketch check` before `ready`. A variant with problems
134
+ cannot be picked. A part in a variant keeps every state the part has in the product: `sketch check`
135
+ and `sketch ready` fail on a dropped state, and warn when two stylesheets of one part define the same class. A problem the product already had prints as `NOTE (was there before)` and fails
136
+ neither. Use `sketch list` to see the open ones.
137
+ `sketch check` also runs tsc over each variant with its `@shared/` and `@system/` imports, so never write a
138
+ tsconfig of your own. It warns of a variant stylesheet that restyles a class of `shared/`, of a
139
+ variant file nothing imports, and of a `shared/` export the variant does not import; `design check` warns of
140
+ an export in a merged part's `shared/` that nothing imports, and (`STALE-REPLACE`) of a `.replace('<…')` or
141
+ `.replace(/<…/)` on markup that neither the part nor a part it uses draws. To delete a file of a part, delete it in the variant: a merge deletes the
142
+ product file the picked variant no longer holds.
143
+
144
+ Each variant's note, what it is and what it costs, goes in with `sketch new --variant-note a=<text>`, once
145
+ per variant: `sketch.json` keeps it apart from the part's `definePart({ note })`, and Studio shows it in the
146
+ variant's head. A merge keeps the part's note, takes its `--note`, or gives a new part the picked variant's note.
147
+ `sketch new` and `sketch variant` write their files in the checkout's Prettier format; after an edit,
148
+ `sketch check --fix` formats the variants before it checks them. A sketch command
149
+ without `--product` takes the product that holds the sketch.
150
+
151
+ Start a new part from a close one with `--like`: `sketch new pages/inbox --like pages/system`
152
+ copies the product's `pages/system` into each variant, renamed to the target and without its
153
+ `madeBy`; `--like pages/system:b` copies variant b of the open sketch on it, with that sketch's
154
+ `shared/` folder.
155
+
156
+ Code the variants of one sketch have in common (sample data, a switch, a helper) goes in the
157
+ sketch's `shared/` folder, next to the variant folders, never into copies per variant. A part in a
158
+ variant imports it as `@shared/<path>`, and a shared file may import `@system/...` and
159
+ `@shared/...`. A part outside a sketch may not import `@shared/`. A variant that needs a shared file
160
+ changed holds its own copy at `<variant>/shared/<path>`; `@shared/<path>` finds that copy first, and a merge
161
+ of that variant takes it. On a merge, `shared/` moves into
162
+ the target's folder as `shared/` with its imports made relative; when no part imports it, it is
163
+ dropped. Only the target may import it: a merge refuses while another part of the picked variant
164
+ does, so move that code into the part. `sketch new` prints each file it puts in `shared/`, for example
165
+ the `shared/` folder of a merged page: edit it there, as part of the sketch, not in a variant copy.
166
+
167
+ ```ts
168
+ // sketches/pages-flows/shared/flow-data.ts
169
+ export const flows = [{ name: 'Checkout', steps: 4 }];
170
+
171
+ // sketches/pages-flows/a/pages/flows/flows.ts
172
+ import { flows } from '@shared/flow-data';
173
+ ```
174
+
175
+ An asset (a mark, logo, icon, picture or font) is one file in `.ethlete/design/<product>/assets/`,
176
+ never a copy inlined into parts. A part imports it as `@assets/<file>`: the default import is a URL
177
+ for `<img src>`; an SVG that must follow tokens (`currentColor`, `var(--accent)`) imports with
178
+ `?raw` and goes inline, for example `@assets/icons/<name>.svg?raw`. A stylesheet, the product's
179
+ `styles.css` or a part's, uses `url('@assets/<file>')`, for example in an `@font-face`. A foundation
180
+ part may wrap an asset in a named export. A product finds its base product's assets too. A sketch
181
+ variant adds or changes an asset in its own `<variant>/assets/` folder (`sketch new` and `sketch variant`
182
+ print it next to the part file), and a merge copies it into the product's. Size an inline SVG with
183
+ `svgAsset(mark, { size: 32 })` from `@design-system`, never by replacing its `width`/`height` as a
184
+ string; `class` and `attrs` (`{ 'aria-hidden': 'true' }`) set the rest of the root's attributes. `design check` fails an SVG asset with no root `<svg>` or without
185
+ `xmlns="http://www.w3.org/2000/svg"` (an `<img src>` and an image viewer do not draw it). The part hash behind `design check` and "changed outside a sketch" counts the bytes of
186
+ the assets a part uses.
187
+
188
+ Every change to a merged part goes through a sketch, also a small one: new copy, sample data, an
189
+ added state. When there is nothing to choose, the sketch has one variant (`--variants a`), and the
190
+ user merges it or comments on it. Never edit a merged part directly, or the user cannot compare or
191
+ comment on the change in Studio. `design check` warns, and Studio marks the part "changed", when a
192
+ merged part changed outside a sketch; only a merged sketch on that part clears it.
193
+
194
+ A sketch on a foundations part such as `foundations/tokens` keeps the part's overview drawing: the
195
+ variant adds its token group as a state, or extends that group's state. It never replaces the
196
+ drawing with a demo scene, since the merge would drop every other group. A demo may be an extra state.
197
+
198
+ Name the variant you work on with `--variant`. Studio marks it as drawing, and hides its frames,
199
+ while the latest report names it. `sketch new` starts no report, so `report --done` follows only a report
200
+ of your own; it prints the next steps. `sketch new`, `sketch variant` and each report record your
201
+ model id as an author of the variants they write, and `sketch ready` records it on every variant
202
+ that has no author yet. A merge adds the picked variant's authors to the `madeBy` the part file
203
+ already holds, and never removes it.
204
+
205
+ ### Combine variants in a new round
206
+
207
+ Studio never combines variants. When the user asks for one in a comment ("solid like a, outline
208
+ like b"), draw it as a new variant in a new round:
137
209
 
138
- A `<script>` in a drawing's `body` never runs. For behavior - a play button that restarts a
139
- CSS animation, a click that toggles a class - give the drawing a `setup(root)` function. The
140
- frame calls it once, after the markup is in `root`. `setup` has no cleanup hook, because each
141
- frame load is fresh. Wire behavior with one delegated listener on `root`. `setup` can also be an array of functions, run in order. If one throws, the
142
- markup stays and the error shows on top of it.
210
+ ```bash
211
+ et design sketch variant <layer>/<name> c --from a --by <model id> --round "Solid a, outline b" --note "r1 showed pill and square; the user wants a's solid with b's outline"
212
+ ```
213
+
214
+ `--from` copies a variant into the new one, sets the sketch back to in progress and clears the
215
+ pick; it refuses while the decision is a discard (the user picks a variant first). Without `--round` the new variant joins the running round; with it, `--note` is required and
216
+ goes on the running round: what it showed and why its variants lost. Older variants stay pickable.
217
+ The command prints the notes of the previous round. Fix a round's title or note later with `sketch round <layer>/<name> <round> --title … --note … --by <model id>`; `sketch list` shows each sketch's rounds.
218
+ Letters continue across rounds (r1: a, b; r2: c, d). `sketch variant <layer>/<name> c,d --from a` adds
219
+ several variants at once; with `--round` they all start the new round. A new round answers the comments on the
220
+ `--from` variant and marks them "addressed in round" with the new round's key. A variant without `--round` fails while the sketch has open comments and lists them: a variant that answers a comment starts a new round. Draw the combined
221
+ variant and mark the sketch `ready` again. `sketch new --round <title> --note <text>` names
222
+ round 1 and sets its note; `--round` without `--note` fails. `sketch ready` refuses while the running round has no note; set it with `sketch round`.
223
+ `sketch ready` also shoots every state of every variant and of the product's part: when not one pixel differs
224
+ in any of them, it merges the sketch itself and logs it as an `auto` entry in the product's `accepted.json`
225
+ ("No visible change in any state: merged automatically."). Any difference or failed shot leaves it `ready`.
226
+ A problem the product already had before the merge does not block it (the check prints it as a note); only a problem the merge adds does.
227
+ Run `sketch ready` again on a `ready` sketch to repeat the check and the auto merge.
228
+
229
+ ### Your model id: `--by`
230
+
231
+ Every command that writes takes `--by <model id>` and fails without it: `sketch new`, `variant`, `report`,
232
+ `ready`, `rebase`, `merge`, `discard`, `inbox done` and `comment reply`. The id is the exact model
233
+ id as free text, for example `claude-opus-5-5`, `gpt-5.6-sol` or `gemini-3-argon`. Studio shows it
234
+ on the variant, the reply and the part, so the user sees which model made what.
235
+
236
+ **Never guess your id.** Use the one your prompt names. If your prompt names none and you do not
237
+ know your own exact id, ask the coordinator or the user before you write.
238
+
239
+ ### The user decides, Studio merges
240
+
241
+ The user picks a variant, or discards the sketch, in Studio. **Studio merges on the user's click.
242
+ Never run `sketch merge` or `sketch discard` yourself, and never pick a variant.** Both mark
243
+ the open comments on the sketch's variants addressed: the user's click answers them. Comments on the
244
+ product's parts stay. If a part the variant replaces changed
245
+ in the product since the sketch was created, the merge refuses: bring each variant up to date, run
246
+ `et design sketch rebase <layer>/<name>`, and mark it `ready` again. `sketch preview <layer>/<name>
247
+ <variant>` lists the elements and pages a merge changes. The note on a part records why it has its
248
+ current shape. The user's reason is optional: with one, the merge sets the note from it; without
249
+ one, the part keeps the `note` the picked variant file holds, so write that note while you draw.
250
+
251
+ After the user approves a direct edit of a merged part (no sketch), run `et design accept <layer>/<name> --product <p> --by <model id> --note <why>` so `design check` stops warning `changed outside a sketch`. Never accept an edit the user did not approve.
252
+
253
+ To drop a merged part the user no longer wants (folded back into its host, say), run `et design part remove <layer>/<name> --product <p> --by <model id>`: it deletes the folder, its `merged.json` entry and its tsconfig path, and resolves its comments. It refuses while another part imports it or an open sketch targets it. Never `git rm` a part by hand.
254
+
255
+ ### Read the inbox
256
+
257
+ The user comments on a state of a part in Studio, at a point, or on a sketch variant. The sketch of
258
+ an element also shows each page that uses it, with each variant swapped in: a comment there names
259
+ the page as its part and the element's sketch variant, so change the variant, not the page.
260
+
261
+ ```bash
262
+ et design inbox [--json] [--product <p>] # open comments and rebases, then what waits for the user
263
+ et design inbox done <id> --note <text> --by <model id> # set the part note, mark the thread addressed
264
+ et design comment reply <id> --by <model id> <text> [--question] # answer a comment thread (takes --product)
265
+ ```
266
+
267
+ Read the inbox at the start of a session and after each sketch you mark `ready`. Answer a comment
268
+ by changing the sketch or the part, then `inbox done <id> --note "…"`. The note replaces the `note` of the part the comment was made on (for a sketch variant, the product's part), so write it to explain the whole shape of the part, and pass the old note unchanged when it still holds. `done` fails without `--note`; only the user withdraws a comment without one, in Studio. `done` also stores your `--by` where the comment sat: in the authors of the sketch variant, or in the `madeBy` of the part. A pick or discard decision is listed as waiting
269
+ for the user: Studio merges or discards on the user's click, so never run `sketch merge` after a pick.
270
+
271
+ A comment is a thread: the user and you both reply. When a comment is unclear, ask with
272
+ `comment reply --question` instead of guessing. The thread then moves to the "waiting for the user" group
273
+ (`waitingForUser` in `--json`) and returns to the inbox when the user answers. A thread there is
274
+ not work for you.
275
+
276
+ A thread has three states. **Open** waits for an answer. **Addressed** (`addressed`: who, when, why,
277
+ the round) means `inbox done`, a new round, a merge or a discard answered it: it leaves the inbox and
278
+ the check, Studio shows it with an "addressed" badge, and you may still `comment reply` on it to say
279
+ what you changed. A reply by the user opens it again. **Resolved** means the user closed it in Studio -
280
+ only the user resolves. The file stays so the user can look back at it; a reply or `done` on it fails.
281
+
282
+ Pass `--question` only when you cannot go on without the user's answer. Studio lists only those
283
+ threads under "Questions for you". A report of what you did is no question: reply without the
284
+ flag, and the thread shows as "answered" until the user withdraws it or replies.
285
+
286
+ ### Draw a flow
287
+
288
+ A flow joins states of pages into a path the user reads left to right in Studio's Flows view.
289
+ It is `<product>/flows/<name>.json`; `position` and `label` are optional:
290
+
291
+ ```json
292
+ {
293
+ "frames": [{ "part": "pages/library", "state": "empty", "position": { "x": 0, "y": 0 } }],
294
+ "lines": [
295
+ {
296
+ "from": { "part": "pages/library", "state": "empty" },
297
+ "to": { "part": "pages/film-detail", "state": "default" },
298
+ "label": "user adds a film"
299
+ }
300
+ ]
301
+ }
302
+ ```
303
+
304
+ Every flow line is labelled, and the element the label names is visible in the frame before it: a line
305
+ labelled "user clicks 'Log in'" starts from a frame that shows a "Log in" button. A line that names
306
+ nothing the reader can see, or a label for a control the page lacks, is a defect.
307
+
308
+ A line may join two states of one page. A frame may name a page that exists only as an open sketch;
309
+ the flow then shows its first variant. Lines hold for every preset. Without `position`, Studio
310
+ lays the frames out along the lines.
311
+
312
+ ### Draw a wireframe
313
+
314
+ A wireframe exists to iterate fast, in minutes per round, rough enough to redraw with a client at
315
+ the table. It answers one thing: which regions a screen has and what each region holds. These
316
+ rules are strict:
317
+
318
+ - **Keep what exists.** Before a sketch, list what the running app already does on that screen.
319
+ Keep it as a labelled box, and draw only what is still open.
320
+ - **One sketch per screen or flow**, never one per region or per detail. The variants differ in
321
+ layout: where the regions sit and how big they are.
322
+ - **Regions and names only.** A region is a box with a label; a list shows item names. Draw no
323
+ counts, badges, status markers, inspectors, size labels or field lists. If a detail matters,
324
+ write it as a word in the region label.
325
+ - **Never polish.** Do not fix alignment, spacing or a kit gap. When the kit lacks a shape, draw a
326
+ labelled box. Look at each shot only for a broken frame: overflow, a scroll bar, clipped text.
327
+ - **Draw it yourself.** A wireframe round is small work: no subagent, no friction report.
328
+ - **Stop early.** When the regions of the main screens are settled, the stage ends. Comments on
329
+ the look of a wireframe belong to the next stage, not to a new wireframe round.
330
+
331
+ A page may not import another page. To show a page inside a page ("in context"), embed it:
332
+ `embedPage('pages/system', { state, width, height, scale })` from `@design-system` answers a
333
+ drawing whose `body` is a frame of that page, drawn in the same product, sketch variant and theme
334
+ as the embedding frame. `width` and `height` are the viewport the page is drawn at (default
335
+ 1280 x 800; `height: 'auto'` fits its content), `scale` shrinks the frame. Never rebuild a page
336
+ from its elements to show it.
337
+
338
+ A page that has no parts yet draws grey stand-ins from the wireframe kit; pages and elements may
339
+ import it, components and foundations may not:
340
+
341
+ ```ts
342
+ import { box, heading, textLines, image, button, input, icon, select } from '@design-system/kit';
343
+ ```
143
344
 
144
- For a prototype with states, render each state with `ngStates({ closed: …, open: … }, { imports })`
145
- and put its `body` in the drawing. `setup: flow.on([{ click: '.trigger', go: 'open' }])` swaps the
146
- states on a click, in a View Transition. To draw an open overlay, open it in
147
- the template (`<et-select [open]="true">`, `<div etMenu [open]="true">`): `ng()` returns the real
148
- panel, and the frame places it below its trigger. See `components/ssr/02-open-overlays`. `ngStates` wraps the states in a
149
- `<div data-de-states style="display: contents">` host. So `flow.body` must be the drawing's body,
150
- or contain it. A selector rule must not depend on that wrapper. See `components/ssr/01-states`.
345
+ Signatures are in `libs/cli/design/wireframe-kit.ts`: `box(children, { direction, gap, padding, width, height, grow, plain, stackBelow, fill })`,
346
+ `heading(text, { size })`, `textLines({ lines, width, size })`, `image({ width, height, ratio })`,
347
+ `button(label, { primary })`, `input({ label, placeholder, width })`, `icon({ size, glyph })`,
348
+ `select({ label, value, width })`, `tabs(labels, { active })`, `segmented(options, { active })`,
349
+ `checkbox(label, { checked })`, `badge(text)` and `dot({ hollow, size })`. Draw a control as its
350
+ type: a picker is a `select`, a section switch `tabs`, a size switch `segmented`, a count a `badge`,
351
+ a status a `dot`, never a row of buttons or an empty `icon`. Give an icon a `glyph` (`'plus'`,
352
+ `'close'`, `'menu'`, `'more'`, …; another name draws its first letter). Give the root box of a page or a
353
+ shell `fill: true` so it is as tall as the frame, instead of a hand-written `min-height`. Draw for
354
+ every preset, not one width: give a picture a `ratio` (`'2/3'`) instead of a fixed height, and give
355
+ a row `stackBelow: <px>`, the width below which it stacks its children, instead of fixed widths
356
+ that only fit the desktop. Never use `vh`, `dvh` or `svh` in a part: a part has no viewport of its own, so the unit measures Studio's frame, not the page. A full page is a frame of a fixed size. Put titles in `heading`, never in hand-written markup. Studio lists such a page under
357
+ "Wireframes" with its kit-use count. A shell is a normal element named by purpose (`shell-main`); draw a rough one with the kit, so each
358
+ page imports the shell instead of repeating it, and a shell pick lands in one file.
359
+ A dialog without its own URL is an element that a page state shows ("login open"). Examples in
360
+ `.ethlete/design/studio-lab`: `pages/film-detail`, `elements/shell-main`, `elements/login-dialog`,
361
+ `flows/sign-in.json`.
362
+
363
+ ### Check a product
364
+
365
+ ```bash
366
+ et design check --product <name> # layer dependencies, hard-coded colours, undefined tokens, copied components, scripted motion, tsc (open sketch variants too), render
367
+ et design system --json # each part's origin, note, states, presets and problems
368
+ ```
369
+
370
+ Without `--product`, `check` covers every product. A part may only use its own layer and the
371
+ layers below it (see "The order of a design"). Rule `FLOW` reports a misshaped flow file, a frame
372
+ naming a part or state that exists neither in the product nor in an open sketch of that part, a duplicate state, and a line to a state that is not a frame of
373
+ the flow. Rule `WIREFRAME-KIT` reports a component or foundation importing the kit. Pass the changed files to the check below as before.
374
+
375
+ Once the other rules pass, the check draws every state in a headless Chromium on a server of its own
376
+ (pages at each preset, a part with `widths` at each width, any other part fitted), and
377
+ `sketch check` does the same for the open variants and the pages they show in. Each line names
378
+ product, part, state, size and a selector path:
379
+
380
+ - `OVERFLOW` - the frame scrolls: content wider than the frame, or taller than a preset height.
381
+ - `CLIPPED` - an element with `overflow: hidden|clip` cuts its content, unless it sets
382
+ `text-overflow: ellipsis` or a line clamp.
383
+ - `WRAP` - text in a button, tab, badge, chip or `white-space: nowrap` box runs onto a second line.
384
+ - `ZERO-SIZE` - the part root has no width or no height.
385
+ - `UNKNOWN-THEME` - a `et-color--<name>` class names a colour theme no stylesheet of the frame defines, so it draws in the fallback colour.
386
+ - `RENDER` - the frame refused to draw, or the part did not load.
387
+
388
+ A `WARNING` line does not fail the check:
389
+
390
+ - `UNDEFINED-CLASS` - a class carries a prefix the product's part CSS uses (`st-`, not the SDK's
391
+ `et-`), but no stylesheet of the frame defines it. Named once, at the first part that draws it.
392
+ - `GRID-COVERAGE` - some `grid` rows fill every interaction column (`rest`, `hover`, `active`,
393
+ `focus-visible`, `disabled`) and others leave some out, so a state can break there unseen.
394
+
395
+ A part that breaks a rule on purpose names it: `definePart({ checks: { skip: ['CLIPPED'] }, … })`;
396
+ the skip covers the part wherever it is drawn. Without Playwright Chromium the check prints
397
+ `RENDER CHECK SKIPPED` and passes on the other rules.
398
+
399
+ A project's `config.json` entry can name `themes`, `{ "surface": [...], "color": [...] }`; a
400
+ frame asked for a theme gets the `et-surface--<name>` and `et-color--<name>` classes on its root,
401
+ so a drawing must take its colours from the theme tokens and never set a surface or colour class
402
+ on its own root. An inner panel may set `et-surface--<name>`.
403
+
404
+ `et design [checkout]` serves the frames; the checkout defaults to the working directory and
405
+ `DE_PORT` overrules the port. In the ethlete SDK the user reads Studio, started with `yarn studio`,
406
+ which serves the frames itself: do not start `yarn design` next to it. Studio carries its own copy
407
+ of the tool, so a checkout that installs nothing still draws.
408
+
409
+ Three constraints on a drawing file:
410
+
411
+ - **Never import a package barrel.** The libraries resolve to source, so one barrel makes the
412
+ browser request every module and the requests fail with `ERR_INSUFFICIENT_RESOURCES`. Import
413
+ the one file you need.
414
+ - **The fixture is shared, and a variant may not change it.**
415
+ - **One file per variant**, so a broken variant breaks its own frame only.
416
+
417
+ A `<script>` in a drawing never runs. For behavior give the drawing a `setup(root)` function
418
+ (called once after the markup is in `root`, no cleanup, wire one delegated listener). Render each
419
+ state of a prototype with `ngStates({ closed: …, open: … }, { imports })` and swap them with
420
+ `setup: flow.on([{ click: '.trigger', go: 'open' }])`. To draw an open overlay, open it in the
421
+ template (`<et-select [open]="true">`); `ng()` returns the real panel and the frame places it.
422
+ `ngStates` wraps the states in `<div data-de-states style="display: contents">`, so a selector
423
+ rule must not depend on that wrapper.
151
424
 
152
425
  **A repository with a sketch Storybook** draws every variant in the **same** story, side by
153
426
  side, under the real geometry the thing ships in. The default Storybook is
@@ -160,15 +433,31 @@ Either way:
160
433
  settled.
161
434
  - Keep the rejected variants drawn, marked as rejected.
162
435
 
436
+ ### Notes
437
+
438
+ A note explains a state: why it looks this way, what it shows. A comment is feedback that needs
439
+ an answer. Write a note when nothing is asked of the reader.
440
+
441
+ A note is `<product>/notes/<timestamp>-<id>.json`:
442
+
443
+ ```json
444
+ { "part": "button", "state": "disabled", "point": { "x": 24, "y": 8 }, "size": "mobile", "text": "Muted, not hidden." }
445
+ ```
446
+
447
+ - `part`, `state` and `text` are required. `point` is optional, in CSS pixels.
448
+ - `size` is required when `point` is set: the preset, the CSS pixel width, or `"fitted"` the note was written at. A
449
+ pointed note shows only at that size; a note without a point shows at every size.
450
+ - A free note on a flow is an entry in the `notes` array of `flows/<name>.json`:
451
+ `{ "id": "…", "text": "…", "at": { "x": 0, "y": -40 } }`. `at` is optional, the note's top-left
452
+ corner on the flow canvas, and may be negative.
453
+
163
454
  ## The order of a design
164
455
 
165
456
  A product design runs in this order. Each stage settles in calls before the next starts, and
166
- each stage draws with what the stages before it settled:
457
+ each stage draws with what the stages before it settled (the wireframes draw with the kit only):
167
458
 
168
- 1. **Foundations**: colours, colour themes and surface themes (the project's `themes` in
169
- `config.json`), plus the type scale, spacing and radius.
170
- 2. **Logo and CI**, if the project needs them.
171
- 3. **Wireframes**: early, rough concepts of the main flows (`mode: 'wireframe'`). They show
459
+ 1. **Wireframes**: early, rough concepts of the main flows (a legacy call with `mode: 'wireframe'`;
460
+ in a product, draw them with the wireframe kit, see "Draw a wireframe"). They show
172
461
  which components the product needs, not how they look. The stage ends with an inventory in
173
462
  the wireframe call: every part the wireframes need, each mapped to the SDK component it
174
463
  starts from, or marked new with the component call that draws it. The inventory is the
@@ -181,7 +470,10 @@ each stage draws with what the stages before it settled:
181
470
  ],
182
471
  ```
183
472
 
184
- 4. **Components**, from the SDK ones rendered with `ng()`, themed and set up for the project.
473
+ 2. **Foundations**: colours, colour themes and surface themes (the project's `themes` in
474
+ `config.json`), plus the type scale, spacing and radius.
475
+ 3. **Logo and CI**, if the project needs them.
476
+ 4. **Components**, the inventory is their queue, from the SDK ones rendered with `ng()`, themed and set up for the project.
185
477
  Each accepted one becomes a design-system entry.
186
478
  5. **Larger elements**: the shell, the navigation, headers, lists, built from the entries.
187
479
  6. **Pages**, built from the elements and the entries.
@@ -191,27 +483,36 @@ Do not start a stage before the one above it is settled, and do not draw a page
191
483
  are not in the design system. If the user asks for a later stage first, say which earlier stage
192
484
  is missing, and draw that first.
193
485
 
194
- A quick exploration of an idea does not run this order: start it with `--stage sketch`. A
195
- sketch needs no settled stage before it, and Studio lists it apart from the seven stages. When
196
- the idea holds, draw it again at its real stage.
197
-
198
- A call made before the stage was required carries `legacy: true`, which
199
- `et design legacy <project>` writes once. It also writes the stage its group folder implies:
200
- `foundations` is `themes`, `components` is `component`, `pages` is `page`; another folder gets
201
- none. Run it again to add the stage to an old legacy call; a stage already there stays. A
202
- settled legacy call with a stage counts for that stage in this order. Never add `legacy` to a
203
- new call.
486
+ The stages map onto the layers of a product: wireframes, foundations, components, elements, pages. A settled
487
+ Legacy call with a stage counts for that stage in this order.
204
488
 
205
489
  A later stage can show that an earlier one is incomplete. If a page needs a part that the system
206
- does not have, add it to the inventory and open a component call for it. Never draw the part
207
- inline in the page.
490
+ does not have, add it to the inventory. A part becomes a component only when at least two hosts
491
+ draw the same case. Until then it stays in the one element or page that draws it. When a second
492
+ host draws it, open a component sketch, and list in its brief every host and every case each one
493
+ draws, so the component covers them all.
494
+
495
+ A component documents its states per axis, not every combination: for example kind, end mark and
496
+ interaction (rest, hover, focus-visible, selected, inert), plus only the combinations that look
497
+ different. A smaller piece with no states of its own, such as an icon set, is a foundation asset,
498
+ not a component.
499
+
500
+ When each of several cases is drawn at rest and in the same interactions, build the states and their grid with
501
+ `interactionRows(rows, { interactions: ['hover', 'active'], force: (props, interaction) => ({ ...props, interaction }) })`
502
+ from `@design-system` and spread the result into `definePart`; never write those states and the grid by hand.
503
+ `sketch check` prints `grid <rows> x <columns> ok`, or the states the grid leaves out; a state in two cells
504
+ fails it as `GRID`, in `design check` too.
505
+ `definePart({ interactions })` forces an interaction on the part's root only. To draw one element of the part
506
+ hovered, focused, pressed or disabled, such as one row of a list, wrap that element's markup in
507
+ `forceInteraction(markup, 'hover')` from `@design-system` inside `render`, driven by a prop; never write
508
+ `data-et-force` by hand.
208
509
 
209
510
  ## Draw from the design system
210
511
 
211
512
  A call that invents its own button, field or row does not match the calls beside it. **Before
212
513
  you draw a part, look for it in this order, and draw new only when none of them has it:**
213
514
 
214
- 1. The project's design system, `.ethlete/design/system/<project>/`.
515
+ 1. The product's design system, `.ethlete/design/<product>/` (and its base).
215
516
  2. The real SDK components, rendered on the design server with `ng()` from `@design-explore`:
216
517
 
217
518
  ```ts
@@ -244,22 +545,10 @@ you draw a part, look for it in this order, and draw new only when none of them
244
545
  3. Only then a new drawing. Build it from the two above, and say in the variant's `claim`
245
546
  that the part is new.
246
547
 
247
- One entry is one folder:
248
-
249
- ```
250
- .ethlete/design/system/<project>/<entry>/
251
- <entry>.ts exports the drawing function, with typed options; built from the SDK mocks
252
- entry.md what it is, when to use it, which SDK parts and options, do and do not
253
- ```
254
-
255
- Its preview is a view call at `.ethlete/design/calls/<project>/system/<entry>/`, because the
256
- tool reads calls from `calls/` only. A variant imports the entry with a relative path. Read
257
- every `entry.md` of the project before the first drawing of a session.
258
-
259
- **An entry exists only because the user accepted it.** A `chosen` verdict is not enough: a
260
- chosen variant is often a layout or a one-off. When the user says to accept a part into the
261
- system, extract it in one commit: the entry folder, its preview call, and the source call's
262
- variant changed to import it. Never change an entry's look without a call the user settled.
548
+ A part is one folder, `<product>/<layer>/<part>/`, with its drawing and states. Read the parts of
549
+ the product before the first drawing of a session. **A part exists only because the user picked a
550
+ sketch.** A `chosen` verdict in an old call is not enough. Never change a part's look without a
551
+ sketch the user decided.
263
552
 
264
553
  ## Check before you look
265
554
 
@@ -273,7 +562,8 @@ With `et design`, run the checker from the checkout it draws:
273
562
  et design check --lint <changed files> --tsconfig
274
563
  ```
275
564
 
276
- `--tsconfig` with no path types every call of the checkout. `--checkout <path>` names another
565
+ `--tsconfig` with no path types every call of the checkout. `--tsconfig` also types each variant of
566
+ an open sketch and prints the tsc output of a failing one. `--checkout <path>` names another
277
567
  one.
278
568
 
279
569
  With a Storybook, copy {%resource:check-story.mjs%} to the **repository root** first:
@@ -293,11 +583,13 @@ et design check --call <slug> --variant b # one of them
293
583
  node check-story.mjs --tsconfig <path> --story <story-id>
294
584
  ```
295
585
 
296
- ## Delegating a call
586
+ ## Delegating a sketch
297
587
 
298
- Every brief names `model: opus`.
588
+ Every brief names `model: opus`, and every prompt names the exact model id the sub-agent runs on,
589
+ for example "Your model id is `claude-opus-5-5`; pass `--by claude-opus-5-5` to every write
590
+ command." You know the id because you chose the model; the sub-agent does not, and must not guess.
299
591
 
300
- 1. **One agent per call.** It draws all the variants of the call. Give it the call folder, the
592
+ 1. **One agent per sketch.** It draws all the variants of the sketch. Give it the sketch folder, the
301
593
  fixture it must not change, and the claim of each variant. Do not spawn one agent per
302
594
  variant: each would re-read the skill, the call, the fixture and the example, about 45-57k
303
595
  tokens each, so the cost is several times higher. Calls are independent, so several calls
@@ -305,9 +597,9 @@ Every brief names `model: opus`.
305
597
  2. **One check-and-fix agent, after the drawing agents return.** Give it the changed files
306
598
  and the call slug. It runs the check above, fixes what it reports, and repeats until the
307
599
  check says `ok`. It may not change what a variant draws, only what stops it rendering.
308
- 3. **One write-up agent, once the user settles the call.** Give it the verdict in the
600
+ 3. **One write-up agent, once the user settles the sketch in Studio.** Give it the verdict in the
309
601
  user's own words and the plan file. It records what won, what lost and why, it sets each
310
- variant's `verdict` in `call.ts`, and it writes that round's `note`. It runs while you open
602
+ variant's `verdict` in an old call's `call.ts` (a product sketch needs none; the merge sets the note). It runs while you open
311
603
  the next call.
312
604
 
313
605
  ## Screenshots
@@ -25,4 +25,9 @@ Commits are **lean** and follow {%commitRuleSource%}:
25
25
  - **Stage only what belongs to the change.** The working tree often carries
26
26
  unrelated in-progress work - `git add` the specific files, never `git add -A`
27
27
  blindly.
28
+ - **In a checkout that other sessions share**, commit with
29
+ `git commit -- <paths>`, so their staged files stay out. `git commit -- <path>`
30
+ refuses an untracked file: run `git add -N <that file>` for that one path
31
+ first. Pass a formatter or `eslint --fix` the exact paths you changed, never a
32
+ list from `git diff` or `git status` - that list holds other sessions' files.
28
33
  - Don't push unless asked.
@@ -32,3 +32,20 @@ The session is done when the frontier is empty: every branch of the design tree
32
32
  ---
33
33
 
34
34
  Vendored from [mattpocock/skills](https://github.com/mattpocock/skills) at `3cca18b`, MIT (Copyright (c) 2026 Matt Pocock).
35
+
36
+ ## Local addition: write each question so it reads alone
37
+
38
+ This section is ours, not upstream.
39
+
40
+ - **One question, one situation.** Say what the thing is and why the decision matters, in plain
41
+ words, before the choices. The user must understand it without the earlier rounds.
42
+ - **No reference chains.** Never lean on other question numbers, ADR numbers, slice numbers or
43
+ plan shorthand ("as Q6 settled", "per ADR 07"). Name the actual thing instead.
44
+ - **Remind the user of a concept** when a round starts a new topic: one or two sentences, with a
45
+ concrete example ("a sketch is a draft with several variants of one part, for example three
46
+ headers").
47
+ - **Check the facts before you recommend.** A recommendation built on an assumed file format or
48
+ an assumed existing tool is worse than none. Look it up first, and say in the round when a
49
+ look-up proved an earlier recommendation wrong.
50
+ - **Close with the shared state.** When the frontier is empty, list every decision in one short
51
+ summary and ask the user to confirm it before you write it anywhere.
@@ -87,6 +87,10 @@ If none of them holds, write no file. Tell the user in two or three lines what l
87
87
  the commits, and say a fresh session needs nothing from this one. A handoff nobody resumes is
88
88
  noise, and it costs the next session the tokens to read it.
89
89
 
90
+ Then give a prompt the user can copy into the fresh session, in a code block: the next task, the
91
+ plan file or finding it lives in, and any rule of this session that the repo does not record. Keep
92
+ it to a few lines. When no next task is known, say so instead.
93
+
90
94
  Write `{%handoffDir%}/<slug>.md` where `<slug>` is a short kebab-case name for
91
95
  the task (use the user-provided slug if they gave one). If the file exists,
92
96
  overwrite it - a handoff always describes the _current_ state.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ethlete/agent-rules",
3
- "version": "0.1.0-next.26",
3
+ "version": "0.1.0-next.27",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/ethlete-io/ethdk",