docs(skills): teach the incremental check loop in the papercraft skill

The skill's lint step now says to lint only what changed between edits
(rev, changedOnly, since), to read geometry with get_bounds and changes with
get_changes, and to finish with one full lint and a screenshot per mode. The
several-agents section uses get_changes {exclude: "self"}, and the cheat
sheet lists both new tools and the overflow rule. This replaces the pending
snippet file.

The reference and docs/agents.md now match the tools as merged: lint_design
reports coverage under `covered`, and get_bounds returns columns and rows
with continueWith when truncated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Marcos Paulo
2026-10-28 18:53:27 -03:00
parent 27e6765a43
commit 580fa9ecc3
4 changed files with 12 additions and 15 deletions
+2 -2
View File
@@ -114,8 +114,8 @@ On a big page a full `lint_design` after every edit repeats the same output, and
* Every write tool result includes `rev`, the document revision after the call. `get_basic_info` reports the current `rev`. Reads never change it.
* `get_changes {since, page?}` returns `{rev, since, truncated, changes: [{page, ids}]}`: what the edits since `since` touched. `truncated: true` means the log no longer reaches that far back, so do a full pass.
* `lint_design {changedOnly: true}` lints since this client's last lint; `{since: rev}` lints since a revision you name. A scoped lint checks the touched layers plus their ancestors, descendants, siblings and every layer bound to a changed variable, and reports its coverage, for example `{checked: 12, total: 397, since: 40, partial: true}`. Plain `lint_design` is a full pass (`partial: false`). The `overflow` rule flags a layer that extends past its parent or clipping artboard (scroll frames are excluded).
* `get_bounds {nodeIds?, pageId?, depth?}` returns `[{id, parent, x, y, w, h, absX, absY, clip}]` for a subtree in one call (capped, with `truncated`). Use it instead of looping `get_node_info`.
* `lint_design {changedOnly: true}` lints since this client's last lint; `{since: rev}` lints since a revision you name. A scoped lint checks the touched layers plus their ancestors, descendants, siblings and every layer bound to a changed variable, and reports its coverage in `covered`, for example `{checked: 12, total: 397, since: 40, partial: true}`. Plain `lint_design` is a full pass (`partial: false`). The `overflow` rule flags a layer that extends past its parent or clipping artboard (scroll frames are excluded).
* `get_bounds {nodeIds?, pageId?, depth?}` returns `columns` `[id, parent, x, y, w, h, absX, absY, clip]` and one row per layer for a subtree in one call (at most 2000 rows; then `truncated` and `continueWith`, the nodeIds for the next call). Use it instead of looping `get_node_info`.
* Always finish with one full `lint_design` (no arguments) and a screenshot per artboard in each theme mode before reporting. A partial lint speeds up the loop; it does not replace that pass.
In the app, read-only calls no longer draw per-layer outlines or keep the "Working" pill lit for long. The agent workflow, with an example, is in [incremental-checks.md](../skills/papercraft/references/incremental-checks.md).
-9
View File
@@ -1,9 +0,0 @@
# Pending snippet for skills/papercraft/SKILL.md
Paste these three lines into section 2 ("The loop: build, look, lint, fix"), after step 5, then delete this file.
```md
Between edits, lint only what changed: note `rev` from each write result, then `lint_design {changedOnly: true}` (or `{since: rev}`); use `get_changes {since}` to see what an edit touched and `get_bounds` instead of looping `get_node_info`.
Always finish with one full `lint_design` (no arguments, `partial: false`) and a screenshot per artboard in each theme mode before reporting.
Details and an example: [incremental-checks.md](references/incremental-checks.md).
```
+6 -1
View File
@@ -33,6 +33,8 @@ papercraft-cli call get_basic_info # fails with exit 1 if the app is not ru
3. **Build in small steps.** `create_artboard`, then one `write_html` per visual group (header, card, row) so the user sees progress. Name every layer with `layer-name`.
4. **Look.** `get_screenshot` of the artboard after each group or two. Scale 2 to read small text. Read it as a designer: hierarchy, alignment, spacing rhythm, clipping, wrapping.
5. **Lint.** `lint_design` on the artboard. Fix every error and warning; consider the `info` items. Run it again until errors and warnings are zero.
Between edits, lint only what changed: keep the `rev` each write returns and run `lint_design {changedOnly: true}` (or `{since: rev}`). `get_changes {since}` shows what an edit touched; `get_bounds` measures a whole subtree in one call instead of looping `get_node_info`.
Always finish with one full `lint_design` (no `since` or `changedOnly`: `covered.partial` is false) and a screenshot per artboard in each theme mode before reporting. Details: [incremental-checks.md](references/incremental-checks.md).
6. **Fix with the smallest edit.** `update_styles` and `set_text_content` on the existing nodes, not a rewrite. Rewrite a group with `write_html` mode `replace` only when its structure is wrong.
7. **Finish.** `finish_working_on_nodes` (saves). Tell the user what you made in a sentence or two, with artboard names, not ids.
@@ -44,6 +46,7 @@ Lessons from redesigning a 3-page, 130-screen project with up to six agents at o
* **Split by section, not by page.** Give each agent named sections (frame ids) and forbid the rest. New sections go below the current lowest one; re-read positions right before creating, since another agent may have just added one.
* **Section layout**: a section frame holds a title and rows of screen slots (a route label above a fixed-size screen). Keep 640 px between screens and rows and 1600 px between sections, so notes fit beside screens. Stack sections in one column.
* **See what the others changed** before building on their work: `get_changes {since: <your last rev>, exclude: "self"}` lists layers by the client that changed them. `truncated: true` means re-read everything.
* **Recreate, then delete.** Build the new screen next to the old one, finish it, then delete the old one, and record the deleted ids and names in the report. A cleanup agent once deleted buttons and lost icons by editing in place.
* **Reuse the design system across pages**: `create_instance` / `swap_instance` / `<x-paper-instance>` take a main from any page and stay linked. Never `import_components` to use a component: that copies mains onto the page, and instances made from the copies stop following the design system. `lint_design` flags hand-made look-alikes as `not-an-instance` (a different icon is a different component); fix with `write_html` mode replace and the `<x-paper-instance>` it names.
* **QA pass after a build** (screen by screen at scale 1, one call at a time): text layers with stale fixed widths clip labels ("In prog", "New sess"): set `width: fit-content`. Sidebars and panels fixed at 900 px stop short on taller screens: make them fill the height and let the screen hug with a 900 px minimum. Tags and chips overflowing their box, cards overlapping buttons, footers not pinned, leftover `Text` / `Frame` names, gendered copy about people whose gender is unknown (use their name or neutral wording).
@@ -100,6 +103,8 @@ Full flows: [recipes.md](references/recipes.md).
| Know the project, pages, artboards | `get_basic_info`, `get_tree_summary`, `get_children` |
| Know what is selected | `get_selection` |
| Inspect one node | `get_node_info`, `get_computed_styles`, `get_jsx` |
| Sizes and positions of a subtree | `get_bounds` (one call, `columns` + `rows`; `continueWith` when `truncated`) |
| What changed since a revision | `get_changes {since}` (`exclude: "self"` for other agents' edits) |
| Find nodes by text or style | `find_nodes` (text contains the phrase, any case, every page) |
| Read all copy in a subtree | `get_tree_summary {nodeId, fullText: true}` (depth counts from the node: rows under a "Screens" frame need depth 3) |
| See it | `get_screenshot` (`nodeId`, `scale` 1 or 2, `annotations: true` to include notes, `mode` to preview a theme without switching) |
@@ -126,7 +131,7 @@ Full flows: [recipes.md](references/recipes.md).
| Hand work to the helper agent | `delegate` (`role` qa, read or build, a self-contained `task`; only when Settings, Chat, Helper is set) |
| Design rules (design-playbook) | `design_rules` (`query` or `rule` id; needs the `playbook` CLI; cite ids in the report) |
| Report a PaperCraft problem | `report_issue` (a draft the user sends; synthetic repro only) |
| Check quality | `lint_design` |
| Check quality | `lint_design` (`changedOnly: true` or `since` between edits, a full pass at the end; `overflow` flags layers past their artboard or clipping parent) |
| Export | `export` (PNG, SVG, PDF to files), `export_combined_pdf` |
| Point at things for the user | `select_nodes` (`zoom: true`) |
| Take back | `undo`, `redo` |
@@ -14,7 +14,7 @@ Check what you just changed between edits, and check everything once at the end.
2. **Edit.** Make the change (`write_html`, `update_styles`, `set_text_content`, ...). Note the new `rev` from the result.
3. **Lint what changed.** `lint_design {"changedOnly": true}` checks since this client's last lint. `lint_design {"since": <rev>}` checks since a revision you name. A scoped lint covers the touched layers plus their ancestors, descendants and siblings, and every layer bound to a changed variable. The result says how much it covered, for example `{"checked": 12, "total": 397, "since": 40, "partial": true}`.
4. **See what an edit really touched.** `get_changes {"since": <rev>}` returns `{rev, since, truncated, changes: [{page, ids}]}`. Add `page` to limit it to one page. If `truncated` is `true`, the log no longer reaches that far back: do a full pass instead of trusting the list.
5. **Measure in one call.** `get_bounds {"nodeIds": [...]}` (or `pageId`, with `depth`) returns `[{id, parent, x, y, w, h, absX, absY, clip}]` for the subtree. If `truncated` is `true`, narrow `nodeIds` or lower `depth`. Never loop `get_node_info` to read sizes or positions.
5. **Measure in one call.** `get_bounds {"nodeIds": [...]}` (or `pageId`, with `depth`) returns `columns` `[id, parent, x, y, w, h, absX, absY, clip]` and `rows`, one array per layer, parents first. If `truncated` is `true`, call it again with `continueWith` as `nodeIds` (same `depth`). Never loop `get_node_info` to read sizes or positions.
6. **Finish with the full check.** Before you report, always:
* run `lint_design` with no arguments (a full pass, `partial: false`) and fix every error and warning, including `overflow`;
* take a `get_screenshot` of every artboard you touched in each theme mode (`mode`, scale 2).
@@ -31,7 +31,7 @@ write_html {"targetNodeId": "a1:1", "mode": "insert-children", "html": "<div cla
// {"rev": 41, ...}
lint_design {"changedOnly": true}
// {"errors": 0, "warnings": 1, "coverage": {"checked": 12, "total": 397, "since": 40, "partial": true}, ...}
// {"errors": 0, "warnings": 1, "covered": {"checked": 12, "total": 397, "since": 40, "partial": true}, ...}
update_styles {"nodeIds": ["a1:7"], "styles": {"width": "320px"}}
// {"rev": 42, ...}
@@ -40,7 +40,8 @@ get_changes {"since": 41}
// {"rev": 42, "since": 41, "truncated": false, "changes": [{"page": "p1", "ids": ["a1:7"]}]}
get_bounds {"nodeIds": ["a1:1"], "depth": 3}
// [{"id": "a1:1", "parent": "p1", "x": 0, "y": 0, "w": 390, "h": 844, "absX": 0, "absY": 0, "clip": true}, ...]
// {"columns": ["id", "parent", "x", "y", "w", "h", "absX", "absY", "clip"],
// "rows": [["a1:1", null, 0, 0, 390, 844, 0, 0, true], ...]}
lint_design {}
// full pass, "partial": false, zero errors and warnings