From ac2a41391cde91435d0eef7243b1404f26f1ad98 Mon Sep 17 00:00:00 2001 From: Ben MacLaurin Date: Sat, 27 Jun 2026 21:46:46 -0700 Subject: [PATCH] docs: add designing-for-agents skill + no-icon-name-switch rule contract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Synthesizes the design-to-code skills (paper-flex, paper-to-code-components, reconcile-paper-sync, tailwind-token-consolidation) into generic, tool-agnostic "designing with agents" guidance — the layer above the deterministic rules. - .agents/skills/designing-for-agents: agent failure-mode catalog, the rule-vs-guidance decision, authoring techniques, escape hatches, completion gate - docs/rule-research/no-icon-name-switch.md: stage-1 rule contract for the one new deterministic candidate (a component that switch(name)es to return many inline SVGs) - docs/rule-candidates-backlog.md: backlog entries, incl. a narrower re-open of the previously-dropped no-arbitrary-over-bare-value - .gitignore: allowlist the new skill dir (.agents/skills/* is ignore-by-default) Docs/skills/tooling only — no published-package change, so no changeset. rule-writing + rule-validate (detector + OSS evals) are follow-ups. Co-Authored-By: Claude Opus 4.8 (1M context) --- .agents/skills/designing-for-agents/SKILL.md | 109 +++++++++++++++++++ .gitignore | 2 + docs/rule-candidates-backlog.md | 28 +++++ docs/rule-research/no-icon-name-switch.md | 79 ++++++++++++++ 4 files changed, 218 insertions(+) create mode 100644 .agents/skills/designing-for-agents/SKILL.md create mode 100644 docs/rule-research/no-icon-name-switch.md diff --git a/.agents/skills/designing-for-agents/SKILL.md b/.agents/skills/designing-for-agents/SKILL.md new file mode 100644 index 0000000000..73de5a3919 --- /dev/null +++ b/.agents/skills/designing-for-agents/SKILL.md @@ -0,0 +1,109 @@ +--- +name: designing-for-agents +description: Use when deciding what React Doctor should catch or how to frame agent guidance — turning an observed agent failure into either a deterministic rule or a piece of judgment guidance. Catalogs the recurring ways coding and design agents fail, and the authoring techniques that actually change agent behavior. Pairs with rule-research (what to detect) and product-thinking (what to ship). +version: "0.1.0" +--- + +# Designing for Agents + +React Doctor exists for one reason: agents write bad code in predictable ways, and deterministic tooling catches it. This skill is the layer above the rules — the catalog of *how* agents fail and how to turn each failure into a countermeasure that holds. + +Not every agent failure is a rule. A linter can prove `useEffect(async () => …)` is wrong; it cannot prove an agent "reproduced the pixels instead of the data model" or "declared the build done without looking at it." Those are judgment failures. This skill helps you tell the two apart and write the right countermeasure for each. + +The catalog below was mined from hard-won design-to-code and refactor skills. Each lesson is stated generically — it applies to any agent doing substantial work, not one tool. + +## When to use + +- You watched an agent produce bad output and want to systematize the fix. +- You are deciding whether a problem belongs in the **rule pipeline** or in **guidance** (a skill, a prompt, a diagnostic recommendation). +- You are writing a rule diagnostic or a skill and want it to actually change behavior instead of being ignored. + +## The core decision: rule or guidance? + +Classify every observed failure before you build anything. + +| The failure is… | Route it to | Test | +|---|---|---| +| A **deterministic code shape** — provable from the AST or file tree | The rule pipeline (`rule-research` → `rule-writing` → `rule-validate`) | "Could I write a detector that's right every time?" | +| A **judgment lapse** — needs intent, the rendered result, or cross-artifact context | Guidance: a skill, a canonical prompt, or a rule's `recommendation` text | "Does catching this require knowing what the agent *meant* or *saw*?" | +| **Both** — a detectable symptom of a deeper judgment problem | A narrow rule for the symptom **and** guidance for the cause | Most real failures land here | + +Reality check before you reach for a new rule: **the deterministic design surface is already mature.** The `design`, `react-ui`, and `a11y` categories already ship rules for most statically-detectable design mistakes (`no-tiny-text`, `no-gradient-text`, `no-z-index9999`, `no-redundant-size-axes`, …), and the backlog already tracks the obvious next ones. Search before proposing — most of what you observe is already a rule, already backlogged, or already deliberately dropped as too noisy. The genuinely new value is usually in the **guidance** column. + +## The agent failure catalog + +Each entry: the **principle** (what to do), the **failure** it counters, the **countermeasure**, and where it routes. + +### Part A — How agents do the work + +**1. Read the source of truth, not its rendering.** → guidance +Agents reason from lossy, derived views — a screenshot, a summary, a class literal that a transform has already invalidated — and produce output that is plausible and wrong. Identify the authoritative representation, force reading it, and ban inference from the convenient view. Know which oracle answers which question (the render shows what the user sees; the source holds the exact values). + +**2. Reproduce the model, not the surface.** → guidance +When an artifact encodes a model — data, a schema, an intent — agents copy the rendered *form* and lose the *meaning*. A chart becomes a pile of rectangles; dimensions (ranges, segments, states) silently vanish. Recover the underlying model with full dimensionality first. A value with nowhere to live in your model is data you are dropping. + +**3. A plausible substitute is silent drift.** → mostly guidance +Agents accept a close-enough stand-in — a fallback font, the nearest existing token, an eyeballed value — as if it were the real thing. The result looks right and is subtly wrong everywhere. Port the real asset or value; make a missing thing fail *visibly* rather than degrade silently. + +**4. Decompose before you generate; survey the whole before the part.** → guidance +Agents emit monolithic output, discover structure too late, and duplicate patterns that recur across distant parts of the work. "I'll clean it up later" is not a workflow. Require a planning and decomposition pass, and a whole-corpus survey, *before* generation or mutation. + +**5. Reuse before you create.** → both +Agents re-derive what already exists. Require a search of prior outputs, the local codebase, and the ecosystem before producing anything new. This is already a house rule here (`truffler` / `find-similar-functions` / `deslop`); the same discipline applies to every artifact an agent makes, not just utilities. + +**6. Don't collapse distinct things into one parameterized mega-abstraction — and don't abstract deliberate deviations.** → both +Agents abstract by *superficial* similarity: one component that `switch`es on a `name` prop to render ten different SVG bodies; one token that swallows five distinct greys. Abstract shared *structure*; never merge distinct *content/artwork* or intentional one-offs. The boundary is sameness of meaning, not sameness of appearance. + +**7. Match the mechanism's weight to the problem.** → both +Agents reach for heavyweight, runtime mechanisms to solve static problems — a hook and state to toggle a class that never changes at runtime. Use the lightest mechanism that works. State and effects are for behavior, not static appearance. + +**8. Mechanize systematic edits; fix the generator, not the artifact.** → guidance +For a change that must land at N sites, agents hand-edit each one and drift between them; for generated output, they hand-fix the artifact that the next run overwrites. Produce a deterministic transform and apply it mechanically. When output is generated, fix the source. + +**9. Verify the outcome, not a proxy — under controlled conditions.** → guidance +Agents declare done from reading the diff or seeing a green build. Verify the real artifact or behavior; control the comparison so the diff is meaningful (compare like for like). A passing build is necessary, not sufficient — a green check with a broken result is a failed task. + +**10. Distinguish "I got it wrong" from "the target moved" — and never fabricate to close a gap.** → guidance +When output differs from a reference, agents either invent data to make it match or "fix" something that is actually a newer reference. Diagnose *why* the gap exists before acting. Forbid fabrication; require flagging the conflict for a human decision. + +**11. Order work by leverage; collapse symptoms into the root cause.** → guidance +Agents chase many small differences and miss the single high-leverage one. Find and fix the biggest cause first; report one root cause instead of its many echoes. (This is `product-thinking`'s "root causes > individual warnings, fewer findings > more" applied to the agent's own process.) + +### Part B — How to author the countermeasure + +These come from the *structure* every durable agent skill shares. They are how you make a rule diagnostic or a skill actually change behavior. + +**12. State the failure, not just the rule.** Open with the specific bad outcomes the guidance prevents and the good end-state ("RED/GREEN intent"). Agents act on consequences, not abstractions. + +**13. Pre-enumerate the concrete bugs.** Name the specific traps the agent *will* hit, in advance ("the N bugs you will hit"). A named, anticipated failure changes behavior; a generic "be careful" does not. + +**14. Close the escape hatches.** Tabulate the exact rationalization the agent will reach for — "it's simple enough to redo," "I'll componentize later," "the user only asked to paste it" — and pre-rebut each. Unrebutted, every one of these gets taken. + +**15. Give a hard completion gate.** End with a literal "before you call this done, you MUST…" checklist. State that a skipped step is a failed task. + +**16. Rank by corroboration.** When you have several independent sources demanding the same thing, say how many. Higher corroboration is higher confidence — and it is how you decide what to enforce hardest. + +## Escape hatches closed + +| You think | Do this instead | +|---|---| +| "This agent mistake should obviously be a rule." | Most are already a rule, backlogged, or dropped as noisy. Search first; default to guidance for judgment failures. | +| "I'll write a rule for the deeper problem." | A rule proves a code shape. If catching it needs intent or the rendered result, it's guidance — the rule can only catch a symptom. | +| "A medium-FP heuristic rule is good enough to ship." | False positives are correctness bugs here. Run it through `rule-research` and OSS evals before it ships, or keep it as guidance. | +| "Generic advice ('write clean components') will steer the agent." | It won't. Name the specific failure, the specific trap, and the rationalization to rebut. | +| "The lesson is obvious, it doesn't need a completion gate." | The obvious step is the one agents skip. Gate it. | +| "This is a Paper/design-specific lesson." | The principle generalizes. State it for any agent doing the work; keep the example concrete but the rule generic. | + +## Before you call a failure handled, you MUST + +1. Classify it as rule, guidance, or both — and say which. +2. For a rule: confirm it's deterministic and not already covered (search existing rules + backlog), then hand it to `rule-research`. +3. For guidance: state the failure and the good end-state, pre-enumerate the concrete traps, close the escape hatches, and add a completion gate. +4. Tie the countermeasure to the observed behavior, not to a hypothetical. +5. Note corroboration — how many independent observations demand it. + +## See also + +- `rule-research` → `rule-writing` → `rule-validate` — the pipeline for the rule column. +- `product-thinking` — whether a change earns a permanent place on the product surface. +- `writing-guidelines` — prose voice and tone for the guidance column. diff --git a/.gitignore b/.gitignore index db97f69421..6ca314d14b 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,8 @@ review-*.md !/.agents/skills/product-thinking/** !/.agents/skills/rde-eval/ !/.agents/skills/rde-eval/** +!/.agents/skills/designing-for-agents/ +!/.agents/skills/designing-for-agents/** # Local-only: rule-catalog generators and per-rule prompts. The generated # `rules.json` and the prompts themselves live in the (private) evals repo; diff --git a/docs/rule-candidates-backlog.md b/docs/rule-candidates-backlog.md index 6cfcc36a61..57915b77a2 100644 --- a/docs/rule-candidates-backlog.md +++ b/docs/rule-candidates-backlog.md @@ -164,3 +164,31 @@ Each shipped rule passed an OSS noise sweep (react-use, radix-ui/primitives, exc > Raw per-cluster candidate detail (bad/good for all ~200) was produced in `/tmp/rd-mine/*.md` > (ephemeral). Ask to persist any cluster's full detail into the repo if needed. + +--- + +## From the design-to-code skills (`designing-for-agents` synthesis) + +Deterministic residue of the agent failure catalog in `.agents/skills/designing-for-agents`. Most +of that catalog is judgment (→ guidance, not rules); these are the few statically-detectable ones. + +- **`no-icon-name-switch`** — a component that selects among ≥3 distinct **inline** `` artworks + by a prop discriminant (`switch`/if-chain/object-map), instead of one component per icon. + scope + branch · **med** FP (must distinguish inline-SVG branches from component-reference + branches). Genuinely new (adjacent: `no-giant-component`, `no-multi-comp`). bundle-size cost: a + runtime switch over inline JSX is not tree-shakeable, so every icon ships. **Full stage-1 contract: + `docs/rule-research/no-icon-name-switch.md`.** _(corro: `paper-to-code-components` §3)_ + +- **`no-on-scale-arbitrary-spacing`** — ⚠️ **re-opens the dropped `no-arbitrary-over-bare-value`** + with a narrower, more defensible cut: ONLY sizing/spacing utilities (`w-`/`h-`/`max-w-`/`min-w-`/ + `p-`/`m-`/`gap-`/`inset-`/`top-`/`left-`/…) whose arbitrary bracket value lands exactly on the + default 4px scale (`max-w-[1160px]` → `max-w-290`, `gap-[24px]` → `gap-6`). syntax · **med** FP. + The generic `z-[999]→z-999` version was dropped as too noisy; this scope may clear the bar, but + it MUST pass an OSS noise sweep first and is a **default-off** candidate at best — custom theme + scales and non-default `--spacing` break the 4px assumption. _(corro: `paper-to-code-components` + §6; do not implement without re-validating against the original drop reason.)_ + +- _Lower confidence, not yet a contract:_ **`no-state-for-static-variant`** — a `useState` whose + setter is never called, used only to pick a static presentational variant (→ a prop/constant). + Detectable in principle but FP-prone; route through `rule-research` before committing. + _(corro: `paper-to-code-components` §5 "no hooks for static variants".)_ diff --git a/docs/rule-research/no-icon-name-switch.md b/docs/rule-research/no-icon-name-switch.md new file mode 100644 index 0000000000..95742d20ed --- /dev/null +++ b/docs/rule-research/no-icon-name-switch.md @@ -0,0 +1,79 @@ +# Rule contract: `no-icon-name-switch` + +> Stage 1 (`rule-research`) output. Not yet implemented or validated. Source: the +> "one component per icon" failure mode in `designing-for-agents` (catalog #6). +> Stages 2–3 (`rule-writing`, `rule-validate`) require the dev env + RDE harness. + +Rule definition: +This rule catches a single component that selects among three or more distinct inline `` artworks by branching on a prop discriminant (`name`/`type`/`variant`), instead of one component per icon. + +Runtime reason: +Every branch's SVG body lives in one module, so a bundler cannot tree-shake the unused ones — importing the component pulls every icon into the bundle even when one is used. The shared file also grows without bound and hides distinct artwork behind one identity, so no icon can be reused, typed, or lazy-loaded on its own. + +Detector precision: +Scope-aware (the discriminant must resolve to a parameter/prop) with branch inspection (enumerate the return sites of a switch / if-else chain / indexed object-literal map). + +Evidence: + +- Common agent output in design-to-code work: a `BrandIcon({ name })` with a `switch (name)` returning ten `` blocks (the exact antipattern `paper-to-code-components` §3 was written to stop). +- Bundler behavior: a runtime `switch`/object-index over inline JSX is opaque to dead-code elimination; all branches are retained. A `name → imported component` map keeps each icon in its own module and stays tree-shakeable — the valid counterpart. +- Adjacent existing rules (confirm no overlap before building): `no-giant-component`, `no-multi-comp`, `no-nested-component-definition`. None detect prop-discriminated inline-SVG selection. + +Strong positives: + +- `switch` of inline SVGs keyed by a prop: + ```tsx + function Icon({ name }) { + switch (name) { + case "kimi": return ; + case "openai": return ; + case "gemini": return ; + } + } + ``` +- if / else-if chain returning different inline SVG bodies keyed on a prop. +- object-literal map of **inline** SVG elements then indexed by the prop: + ```tsx + const icons = { kimi: , openai: , gemini: }; + const Icon = ({ name }) => icons[name]; + ``` + +False-positive traps (must stay quiet): + +- `name → component reference` map (the recommended pattern; tree-shakeable): + ```tsx + const icons = { kimi: KimiIcon, openai: OpenAIIcon, gemini: GeminiIcon }; + const Icon = ({ name }) => { const C = icons[name]; return ; }; + ``` +- variant switch that changes `className` / text / size but renders the **same** structure (e.g. a Button with `variant`). +- a two-branch toggle of one icon's states (open/closed chevron) — below the distinct-artwork threshold. +- a single, parameterized SVG built from props (one icon, not a selector). +- a barrel/re-export module of per-icon components. + +In scope for v1: + +- A same-file function/arrow component whose body selects by a single prop/param discriminant among ≥3 branches, where ≥3 branches each return JSX whose root is an inline `` with path-like children (`path`/`circle`/`rect`/`g`/`polygon`). +- Discriminant forms: `switch`, if/else-if chains, and an object-literal-of-inline-SVG indexed by the discriminant. + +Out of scope for v1: + +- `name → imported/local component reference` maps (explicit non-goal; this is the fix, not the bug). +- Cross-file or imported branch bodies. +- Non-SVG content switches. +- Two-branch toggles. +- Dynamic/computed discriminants that cannot be tied to a prop. + +Test seeds: + +- Invalid: switch of 3 inline SVGs; if-chain of 3 inline SVGs; object map of 3 inline SVGs indexed by prop; namespace/aliased prop discriminant. +- Valid: object map of component references; 2-branch SVG toggle; Button variant switch on className only; single parameterized SVG; imported-component map; switch returning non-SVG elements. + +Open questions: + +- Distinct-artwork threshold: 3 or 4 branches? (Pick the value the OSS noise sweep supports.) +- Category + severity: `bundle-size` or `architecture`; almost certainly `warning`. Default-on only if evals are clean; otherwise default-off. +- Is the object-literal-of-inline-SVG form v1 or a v2 follow-up? (Higher detector complexity than the `switch`/if forms.) +- Final name: `no-icon-name-switch` vs `no-svg-switch-component` vs `no-monolithic-icon-component`. + +Validation note (blocks stages 2–3): +Medium false-positive risk — distinguishing inline-SVG branches from component-reference branches is the whole game. Per `rule-research` rules, this MUST clear an RDE OSS noise sweep (`rde-eval`) before it ships; treat any false positive as a correctness bug, not acceptable noise.