diff --git a/.agents/skills/impeccable/SKILL.md b/.agents/skills/impeccable/SKILL.md index df474b2f1..8c54e6343 100644 --- a/.agents/skills/impeccable/SKILL.md +++ b/.agents/skills/impeccable/SKILL.md @@ -1,7 +1,7 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.8 +version: 4.0.0-alpha.9 --- Designs and iterates production-grade frontend interfaces. Real working code, committed design choices, exceptional craft. @@ -13,27 +13,13 @@ Approach every design task as the design lead at a small studio known for giving 1. Run `node .agents/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's PRODUCT.md and DESIGN.md when they exist; follow what it prints, including any `UPDATE_AVAILABLE` directive (ask once, never block). If it reports `NO_PRODUCT_MD`: for `init`, `teach`, `craft`, `shape`, or wording that clearly maps to a from-scratch build flow, divert into `reference/init.md` first, **unless no user can respond** (a one-shot or automated run, or the user said not to ask): then write your own one-paragraph understanding of the product, audience, and the page's job from the brief and continue. For scoped evaluate/refine/fix requests against existing code, never divert into init; the existing code is the context. 2. If the user invoked a sub-command (`audit`, `polish`, `live`, ...), read **`reference/.md`** (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. On unattended runs, `craft` and `shape` collapse into this file's process: decide, record each decision in one line, build, self-review. 3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). -4. If the project is brand-new (no committed tokens, fonts, or brand colors found in step 3), run `node .agents/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed breaks your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this step entirely when step 3 found committed brand colors: identity-preservation wins. - ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this file warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. +**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. -**Existing worlds are sacred.** When the surface already has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside it: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Treat the task as new identity work only when nothing committed exists, or when the user asks for a redesign that discards the current look. A redesign is new work: derive the concept from the subject and the brief, not from the incumbent page's structure or styling. +**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. -**Ground it in the subject.** Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. - -**Plan, self-check, build.** Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. - -**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. - -**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. - -**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. - -**Commit.** Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. - -**Calibration.** AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. +**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Not optional, not skippable under time pressure: producing new identity without it yields the generic default this skill exists to prevent. A redesign is new work; derive the concept from the subject and the brief, not from the incumbent page's structure or styling. `context.mjs` prints this directive when it detects the situation. Scoped fixes inside an existing world don't need the playbook; the craft floor below governs them. ## Craft floor @@ -57,7 +43,7 @@ Calibration for this provider: Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. The brief and the surface decide the mode. PRODUCT.md's `register` field names the family (`brand` covers Persuade and Experience; `product` covers Operate and Read), and `reference/brand.md` / `reference/product.md` carry extended depth for the two families. -**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. +**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). **Operate** (app UI, dashboards, admin, tools; design SERVES the task). A person getting something done: density, scanability, and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. diff --git a/.agents/skills/impeccable/reference/new-work.md b/.agents/skills/impeccable/reference/new-work.md new file mode 100644 index 000000000..f71af8d3c --- /dev/null +++ b/.agents/skills/impeccable/reference/new-work.md @@ -0,0 +1,37 @@ +# New identity work + +You are reading this because nothing committed exists yet (greenfield), or the user asked for a redesign that discards the current look. Either way the task is the same: invent a visual identity that could not be mistaken for anyone else's, and build it to the craft floor. SKILL.md's rules all still apply; this file is the process that produces the identity. + +## Seed + +If the project is brand-new (no committed tokens, fonts, or brand colors found in the code), run `node .agents/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed exists to break your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this entirely when the code already has committed brand colors: identity-preservation wins. + +## Ground it in the subject + +Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. + +## Plan, self-check, build + +Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. + +**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. + +**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. + +**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. + +## Commit + +Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. + +## Type and imagery for new Persuade and Experience surfaces + +Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. (For Operate and Read surfaces the opposite holds: system stacks and workhorse UI faces are legitimate and often correct.) + +Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Verify stock URLs resolve before shipping them. + +## Calibration + +AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. + +Before finishing, verify against SKILL.md's craft floor and run the detector; a bold page that ships mechanical defects is not done. diff --git a/.agents/skills/impeccable/scripts/context.mjs b/.agents/skills/impeccable/scripts/context.mjs index 11f2aabe0..15829a157 100644 --- a/.agents/skills/impeccable/scripts/context.mjs +++ b/.agents/skills/impeccable/scripts/context.mjs @@ -904,6 +904,10 @@ async function cli() { 'reference/init.md and write PRODUCT.md first; for any other (scoped) ' + 'command against existing code, proceed using the code as context and ' + `offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, + 'NEW_WORK: No committed design context was found. If this task produces ' + + 'new design (a build from scratch, or a redesign that discards the ' + + 'current look), you MUST read reference/new-work.md before making any ' + + 'design decision. Scoped fixes to existing code do not need it.', ]; parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { @@ -922,10 +926,22 @@ async function cli() { parts.push(buildMissingTargetDirective()); } const register = extractRegister(ctx.product); - const next = register - ? `NEXT STEP: This project's register is \`${register}\`. You MUST now read \`reference/${register}.md\` before producing any design output.` - : `NEXT STEP: You MUST now read the matching register reference (\`reference/brand.md\` or \`reference/product.md\`) before producing any design output. Pick based on PRODUCT.md above.`; - parts.push(next); + // The register field survives as a family hint (brand = Persuade/Experience, + // product = Operate/Read); SKILL.md's mode section carries the essentials + // inline, so no register-file read is mandated here. What IS mandated: + // the new-work playbook when no committed design system exists yet. + if (!ctx.hasDesign) { + parts.push( + 'NEW_WORK: PRODUCT.md exists but no DESIGN.md was found. If the code ' + + 'has no committed design system either (check the project files), and ' + + 'this task produces new design or a redesign that discards the current ' + + 'look, you MUST read reference/new-work.md before making any design ' + + 'decision. Scoped fixes inside an existing design system do not need it.', + ); + } + if (register) { + parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md; \`reference/${register}.md\` has extended depth for substantial work.`); + } const platform = extractPlatform(ctx.product); const nativeRefs = platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; @@ -933,7 +949,7 @@ async function cli() { const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to the register reference.`, + `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, ); } else if (!platform) { // A `## Platform` section that names something we don't recognize (a diff --git a/.agents/skills/impeccable/scripts/live-browser.js b/.agents/skills/impeccable/scripts/live-browser.js index 616f1290a..b0dcaa404 100644 --- a/.agents/skills/impeccable/scripts/live-browser.js +++ b/.agents/skills/impeccable/scripts/live-browser.js @@ -7105,6 +7105,14 @@ } async function captureAndEmit(el, basePayload, snapshot, rect) { + const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); + + // Plain requests do not send a screenshot to the agent, so capture is + // presentation-only. Wait only for the helper to accept the event before + // starting CPU-heavy capture; this yields the browser task and prevents + // rasterization from delaying the fetch itself. + if (!hasAnnotations) await sendEvent(basePayload); + let screenshotPath; let blob; let paper; @@ -7122,7 +7130,6 @@ // are present. Without annotations the image is pure visual anchoring - // it biases the model toward the current rendering and works against the // three-distinct-directions brief. - const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); if (blob && hasAnnotations) { try { const uploadRes = await fetch( @@ -7140,7 +7147,11 @@ console.warn('[impeccable] annotation upload failed:', err); } } - sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + // Annotated requests must wait for capture + upload because the screenshot + // is semantic input. Plain requests were already dispatched above. + if (hasAnnotations) { + sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + } } // diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index f1b0afa90..4a194ffab 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -12,7 +12,7 @@ { "name": "impeccable", "description": "Design fluency for frontend development. 1 skill with 23 commands (/impeccable polish, /impeccable audit, /impeccable critique, etc.) and curated anti-pattern detection.", - "version": "4.0.0-alpha.8", + "version": "4.0.0-alpha.9", "author": { "name": "Paul Bakaus", "email": "paul@paulbakaus.com" diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index e39cb5364..9f6fcb153 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "impeccable", "description": "Design fluency for frontend development. 1 skill with 23 commands (/impeccable polish, /impeccable audit, /impeccable critique, etc.) and curated anti-pattern detection.", - "version": "4.0.0-alpha.8", + "version": "4.0.0-alpha.9", "author": { "name": "Paul Bakaus", "email": "paul@paulbakaus.com" diff --git a/.claude/skills/impeccable/SKILL.md b/.claude/skills/impeccable/SKILL.md index 9db246687..8645c49ff 100644 --- a/.claude/skills/impeccable/SKILL.md +++ b/.claude/skills/impeccable/SKILL.md @@ -1,7 +1,7 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.8 +version: 4.0.0-alpha.9 user-invocable: true argument-hint: "[craft|shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]" license: Apache 2.0 @@ -19,27 +19,13 @@ Approach every design task as the design lead at a small studio known for giving 1. Run `node .claude/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's PRODUCT.md and DESIGN.md when they exist; follow what it prints, including any `UPDATE_AVAILABLE` directive (ask once, never block). If it reports `NO_PRODUCT_MD`: for `init`, `teach`, `craft`, `shape`, or wording that clearly maps to a from-scratch build flow, divert into `reference/init.md` first, **unless no user can respond** (a one-shot or automated run, or the user said not to ask): then write your own one-paragraph understanding of the product, audience, and the page's job from the brief and continue. For scoped evaluate/refine/fix requests against existing code, never divert into init; the existing code is the context. 2. If the user invoked a sub-command (`audit`, `polish`, `live`, ...), read **`reference/.md`** (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. On unattended runs, `craft` and `shape` collapse into this file's process: decide, record each decision in one line, build, self-review. 3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). -4. If the project is brand-new (no committed tokens, fonts, or brand colors found in step 3), run `node .claude/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed breaks your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this step entirely when step 3 found committed brand colors: identity-preservation wins. - ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this file warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. +**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. -**Existing worlds are sacred.** When the surface already has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside it: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Treat the task as new identity work only when nothing committed exists, or when the user asks for a redesign that discards the current look. A redesign is new work: derive the concept from the subject and the brief, not from the incumbent page's structure or styling. +**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. -**Ground it in the subject.** Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. - -**Plan, self-check, build.** Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. - -**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. - -**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. - -**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. - -**Commit.** Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. - -**Calibration.** AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. +**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Not optional, not skippable under time pressure: producing new identity without it yields the generic default this skill exists to prevent. A redesign is new work; derive the concept from the subject and the brief, not from the incumbent page's structure or styling. `context.mjs` prints this directive when it detects the situation. Scoped fixes inside an existing world don't need the playbook; the craft floor below governs them. ## Craft floor @@ -57,7 +43,7 @@ Build to this floor without announcing it. The design detector (the project hook Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. The brief and the surface decide the mode. PRODUCT.md's `register` field names the family (`brand` covers Persuade and Experience; `product` covers Operate and Read), and `reference/brand.md` / `reference/product.md` carry extended depth for the two families. -**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. +**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). **Operate** (app UI, dashboards, admin, tools; design SERVES the task). A person getting something done: density, scanability, and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. diff --git a/.claude/skills/impeccable/reference/new-work.md b/.claude/skills/impeccable/reference/new-work.md new file mode 100644 index 000000000..13c8f054b --- /dev/null +++ b/.claude/skills/impeccable/reference/new-work.md @@ -0,0 +1,37 @@ +# New identity work + +You are reading this because nothing committed exists yet (greenfield), or the user asked for a redesign that discards the current look. Either way the task is the same: invent a visual identity that could not be mistaken for anyone else's, and build it to the craft floor. SKILL.md's rules all still apply; this file is the process that produces the identity. + +## Seed + +If the project is brand-new (no committed tokens, fonts, or brand colors found in the code), run `node .claude/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed exists to break your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this entirely when the code already has committed brand colors: identity-preservation wins. + +## Ground it in the subject + +Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. + +## Plan, self-check, build + +Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. + +**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. + +**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. + +**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. + +## Commit + +Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. + +## Type and imagery for new Persuade and Experience surfaces + +Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. (For Operate and Read surfaces the opposite holds: system stacks and workhorse UI faces are legitimate and often correct.) + +Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Verify stock URLs resolve before shipping them. + +## Calibration + +AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. + +Before finishing, verify against SKILL.md's craft floor and run the detector; a bold page that ships mechanical defects is not done. diff --git a/.claude/skills/impeccable/scripts/context.mjs b/.claude/skills/impeccable/scripts/context.mjs index 11f2aabe0..15829a157 100644 --- a/.claude/skills/impeccable/scripts/context.mjs +++ b/.claude/skills/impeccable/scripts/context.mjs @@ -904,6 +904,10 @@ async function cli() { 'reference/init.md and write PRODUCT.md first; for any other (scoped) ' + 'command against existing code, proceed using the code as context and ' + `offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, + 'NEW_WORK: No committed design context was found. If this task produces ' + + 'new design (a build from scratch, or a redesign that discards the ' + + 'current look), you MUST read reference/new-work.md before making any ' + + 'design decision. Scoped fixes to existing code do not need it.', ]; parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { @@ -922,10 +926,22 @@ async function cli() { parts.push(buildMissingTargetDirective()); } const register = extractRegister(ctx.product); - const next = register - ? `NEXT STEP: This project's register is \`${register}\`. You MUST now read \`reference/${register}.md\` before producing any design output.` - : `NEXT STEP: You MUST now read the matching register reference (\`reference/brand.md\` or \`reference/product.md\`) before producing any design output. Pick based on PRODUCT.md above.`; - parts.push(next); + // The register field survives as a family hint (brand = Persuade/Experience, + // product = Operate/Read); SKILL.md's mode section carries the essentials + // inline, so no register-file read is mandated here. What IS mandated: + // the new-work playbook when no committed design system exists yet. + if (!ctx.hasDesign) { + parts.push( + 'NEW_WORK: PRODUCT.md exists but no DESIGN.md was found. If the code ' + + 'has no committed design system either (check the project files), and ' + + 'this task produces new design or a redesign that discards the current ' + + 'look, you MUST read reference/new-work.md before making any design ' + + 'decision. Scoped fixes inside an existing design system do not need it.', + ); + } + if (register) { + parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md; \`reference/${register}.md\` has extended depth for substantial work.`); + } const platform = extractPlatform(ctx.product); const nativeRefs = platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; @@ -933,7 +949,7 @@ async function cli() { const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to the register reference.`, + `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, ); } else if (!platform) { // A `## Platform` section that names something we don't recognize (a diff --git a/.claude/skills/impeccable/scripts/live-browser.js b/.claude/skills/impeccable/scripts/live-browser.js index 616f1290a..b0dcaa404 100644 --- a/.claude/skills/impeccable/scripts/live-browser.js +++ b/.claude/skills/impeccable/scripts/live-browser.js @@ -7105,6 +7105,14 @@ } async function captureAndEmit(el, basePayload, snapshot, rect) { + const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); + + // Plain requests do not send a screenshot to the agent, so capture is + // presentation-only. Wait only for the helper to accept the event before + // starting CPU-heavy capture; this yields the browser task and prevents + // rasterization from delaying the fetch itself. + if (!hasAnnotations) await sendEvent(basePayload); + let screenshotPath; let blob; let paper; @@ -7122,7 +7130,6 @@ // are present. Without annotations the image is pure visual anchoring - // it biases the model toward the current rendering and works against the // three-distinct-directions brief. - const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); if (blob && hasAnnotations) { try { const uploadRes = await fetch( @@ -7140,7 +7147,11 @@ console.warn('[impeccable] annotation upload failed:', err); } } - sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + // Annotated requests must wait for capture + upload because the screenshot + // is semantic input. Plain requests were already dispatched above. + if (hasAnnotations) { + sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + } } // diff --git a/.cursor/skills/impeccable/SKILL.md b/.cursor/skills/impeccable/SKILL.md index 37f0658b8..b3596b952 100644 --- a/.cursor/skills/impeccable/SKILL.md +++ b/.cursor/skills/impeccable/SKILL.md @@ -1,7 +1,7 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.8 +version: 4.0.0-alpha.9 license: Apache 2.0 --- @@ -14,27 +14,13 @@ Approach every design task as the design lead at a small studio known for giving 1. Run `node .cursor/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's PRODUCT.md and DESIGN.md when they exist; follow what it prints, including any `UPDATE_AVAILABLE` directive (ask once, never block). If it reports `NO_PRODUCT_MD`: for `init`, `teach`, `craft`, `shape`, or wording that clearly maps to a from-scratch build flow, divert into `reference/init.md` first, **unless no user can respond** (a one-shot or automated run, or the user said not to ask): then write your own one-paragraph understanding of the product, audience, and the page's job from the brief and continue. For scoped evaluate/refine/fix requests against existing code, never divert into init; the existing code is the context. 2. If the user invoked a sub-command (`audit`, `polish`, `live`, ...), read **`reference/.md`** (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. On unattended runs, `craft` and `shape` collapse into this file's process: decide, record each decision in one line, build, self-review. 3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). -4. If the project is brand-new (no committed tokens, fonts, or brand colors found in step 3), run `node .cursor/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed breaks your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this step entirely when step 3 found committed brand colors: identity-preservation wins. - ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this file warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. +**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. -**Existing worlds are sacred.** When the surface already has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside it: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Treat the task as new identity work only when nothing committed exists, or when the user asks for a redesign that discards the current look. A redesign is new work: derive the concept from the subject and the brief, not from the incumbent page's structure or styling. +**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. -**Ground it in the subject.** Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. - -**Plan, self-check, build.** Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. - -**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. - -**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. - -**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. - -**Commit.** Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. - -**Calibration.** AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. +**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Not optional, not skippable under time pressure: producing new identity without it yields the generic default this skill exists to prevent. A redesign is new work; derive the concept from the subject and the brief, not from the incumbent page's structure or styling. `context.mjs` prints this directive when it detects the situation. Scoped fixes inside an existing world don't need the playbook; the craft floor below governs them. ## Craft floor @@ -52,7 +38,7 @@ Build to this floor without announcing it. The design detector (the project hook Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. The brief and the surface decide the mode. PRODUCT.md's `register` field names the family (`brand` covers Persuade and Experience; `product` covers Operate and Read), and `reference/brand.md` / `reference/product.md` carry extended depth for the two families. -**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. +**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). **Operate** (app UI, dashboards, admin, tools; design SERVES the task). A person getting something done: density, scanability, and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. diff --git a/.cursor/skills/impeccable/reference/new-work.md b/.cursor/skills/impeccable/reference/new-work.md new file mode 100644 index 000000000..bd7decef9 --- /dev/null +++ b/.cursor/skills/impeccable/reference/new-work.md @@ -0,0 +1,37 @@ +# New identity work + +You are reading this because nothing committed exists yet (greenfield), or the user asked for a redesign that discards the current look. Either way the task is the same: invent a visual identity that could not be mistaken for anyone else's, and build it to the craft floor. SKILL.md's rules all still apply; this file is the process that produces the identity. + +## Seed + +If the project is brand-new (no committed tokens, fonts, or brand colors found in the code), run `node .cursor/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed exists to break your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this entirely when the code already has committed brand colors: identity-preservation wins. + +## Ground it in the subject + +Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. + +## Plan, self-check, build + +Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. + +**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. + +**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. + +**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. + +## Commit + +Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. + +## Type and imagery for new Persuade and Experience surfaces + +Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. (For Operate and Read surfaces the opposite holds: system stacks and workhorse UI faces are legitimate and often correct.) + +Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Verify stock URLs resolve before shipping them. + +## Calibration + +AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. + +Before finishing, verify against SKILL.md's craft floor and run the detector; a bold page that ships mechanical defects is not done. diff --git a/.cursor/skills/impeccable/scripts/context.mjs b/.cursor/skills/impeccable/scripts/context.mjs index 11f2aabe0..15829a157 100644 --- a/.cursor/skills/impeccable/scripts/context.mjs +++ b/.cursor/skills/impeccable/scripts/context.mjs @@ -904,6 +904,10 @@ async function cli() { 'reference/init.md and write PRODUCT.md first; for any other (scoped) ' + 'command against existing code, proceed using the code as context and ' + `offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, + 'NEW_WORK: No committed design context was found. If this task produces ' + + 'new design (a build from scratch, or a redesign that discards the ' + + 'current look), you MUST read reference/new-work.md before making any ' + + 'design decision. Scoped fixes to existing code do not need it.', ]; parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { @@ -922,10 +926,22 @@ async function cli() { parts.push(buildMissingTargetDirective()); } const register = extractRegister(ctx.product); - const next = register - ? `NEXT STEP: This project's register is \`${register}\`. You MUST now read \`reference/${register}.md\` before producing any design output.` - : `NEXT STEP: You MUST now read the matching register reference (\`reference/brand.md\` or \`reference/product.md\`) before producing any design output. Pick based on PRODUCT.md above.`; - parts.push(next); + // The register field survives as a family hint (brand = Persuade/Experience, + // product = Operate/Read); SKILL.md's mode section carries the essentials + // inline, so no register-file read is mandated here. What IS mandated: + // the new-work playbook when no committed design system exists yet. + if (!ctx.hasDesign) { + parts.push( + 'NEW_WORK: PRODUCT.md exists but no DESIGN.md was found. If the code ' + + 'has no committed design system either (check the project files), and ' + + 'this task produces new design or a redesign that discards the current ' + + 'look, you MUST read reference/new-work.md before making any design ' + + 'decision. Scoped fixes inside an existing design system do not need it.', + ); + } + if (register) { + parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md; \`reference/${register}.md\` has extended depth for substantial work.`); + } const platform = extractPlatform(ctx.product); const nativeRefs = platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; @@ -933,7 +949,7 @@ async function cli() { const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to the register reference.`, + `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, ); } else if (!platform) { // A `## Platform` section that names something we don't recognize (a diff --git a/.cursor/skills/impeccable/scripts/live-browser.js b/.cursor/skills/impeccable/scripts/live-browser.js index 616f1290a..b0dcaa404 100644 --- a/.cursor/skills/impeccable/scripts/live-browser.js +++ b/.cursor/skills/impeccable/scripts/live-browser.js @@ -7105,6 +7105,14 @@ } async function captureAndEmit(el, basePayload, snapshot, rect) { + const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); + + // Plain requests do not send a screenshot to the agent, so capture is + // presentation-only. Wait only for the helper to accept the event before + // starting CPU-heavy capture; this yields the browser task and prevents + // rasterization from delaying the fetch itself. + if (!hasAnnotations) await sendEvent(basePayload); + let screenshotPath; let blob; let paper; @@ -7122,7 +7130,6 @@ // are present. Without annotations the image is pure visual anchoring - // it biases the model toward the current rendering and works against the // three-distinct-directions brief. - const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); if (blob && hasAnnotations) { try { const uploadRes = await fetch( @@ -7140,7 +7147,11 @@ console.warn('[impeccable] annotation upload failed:', err); } } - sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + // Annotated requests must wait for capture + upload because the screenshot + // is semantic input. Plain requests were already dispatched above. + if (hasAnnotations) { + sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + } } // diff --git a/.gemini/skills/impeccable/SKILL.md b/.gemini/skills/impeccable/SKILL.md index 1aaa4f1ea..8344f0430 100644 --- a/.gemini/skills/impeccable/SKILL.md +++ b/.gemini/skills/impeccable/SKILL.md @@ -1,7 +1,7 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.8 +version: 4.0.0-alpha.9 --- Designs and iterates production-grade frontend interfaces. Real working code, committed design choices, exceptional craft. @@ -13,27 +13,13 @@ Approach every design task as the design lead at a small studio known for giving 1. Run `node .gemini/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's PRODUCT.md and DESIGN.md when they exist; follow what it prints, including any `UPDATE_AVAILABLE` directive (ask once, never block). If it reports `NO_PRODUCT_MD`: for `init`, `teach`, `craft`, `shape`, or wording that clearly maps to a from-scratch build flow, divert into `reference/init.md` first, **unless no user can respond** (a one-shot or automated run, or the user said not to ask): then write your own one-paragraph understanding of the product, audience, and the page's job from the brief and continue. For scoped evaluate/refine/fix requests against existing code, never divert into init; the existing code is the context. 2. If the user invoked a sub-command (`audit`, `polish`, `live`, ...), read **`reference/.md`** (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. On unattended runs, `craft` and `shape` collapse into this file's process: decide, record each decision in one line, build, self-review. 3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). -4. If the project is brand-new (no committed tokens, fonts, or brand colors found in step 3), run `node .gemini/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed breaks your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this step entirely when step 3 found committed brand colors: identity-preservation wins. - ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this file warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. +**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. -**Existing worlds are sacred.** When the surface already has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside it: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Treat the task as new identity work only when nothing committed exists, or when the user asks for a redesign that discards the current look. A redesign is new work: derive the concept from the subject and the brief, not from the incumbent page's structure or styling. +**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. -**Ground it in the subject.** Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. - -**Plan, self-check, build.** Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. - -**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. - -**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. - -**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. - -**Commit.** Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. - -**Calibration.** AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. +**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Not optional, not skippable under time pressure: producing new identity without it yields the generic default this skill exists to prevent. A redesign is new work; derive the concept from the subject and the brief, not from the incumbent page's structure or styling. `context.mjs` prints this directive when it detects the situation. Scoped fixes inside an existing world don't need the playbook; the craft floor below governs them. ## Craft floor @@ -53,7 +39,7 @@ Build to this floor without announcing it. The design detector (the project hook Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. The brief and the surface decide the mode. PRODUCT.md's `register` field names the family (`brand` covers Persuade and Experience; `product` covers Operate and Read), and `reference/brand.md` / `reference/product.md` carry extended depth for the two families. -**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. +**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). **Operate** (app UI, dashboards, admin, tools; design SERVES the task). A person getting something done: density, scanability, and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. diff --git a/.gemini/skills/impeccable/reference/new-work.md b/.gemini/skills/impeccable/reference/new-work.md new file mode 100644 index 000000000..669148061 --- /dev/null +++ b/.gemini/skills/impeccable/reference/new-work.md @@ -0,0 +1,37 @@ +# New identity work + +You are reading this because nothing committed exists yet (greenfield), or the user asked for a redesign that discards the current look. Either way the task is the same: invent a visual identity that could not be mistaken for anyone else's, and build it to the craft floor. SKILL.md's rules all still apply; this file is the process that produces the identity. + +## Seed + +If the project is brand-new (no committed tokens, fonts, or brand colors found in the code), run `node .gemini/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed exists to break your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this entirely when the code already has committed brand colors: identity-preservation wins. + +## Ground it in the subject + +Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. + +## Plan, self-check, build + +Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. + +**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. + +**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. + +**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. + +## Commit + +Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. + +## Type and imagery for new Persuade and Experience surfaces + +Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. (For Operate and Read surfaces the opposite holds: system stacks and workhorse UI faces are legitimate and often correct.) + +Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Verify stock URLs resolve before shipping them. + +## Calibration + +AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. + +Before finishing, verify against SKILL.md's craft floor and run the detector; a bold page that ships mechanical defects is not done. diff --git a/.gemini/skills/impeccable/scripts/context.mjs b/.gemini/skills/impeccable/scripts/context.mjs index 11f2aabe0..15829a157 100644 --- a/.gemini/skills/impeccable/scripts/context.mjs +++ b/.gemini/skills/impeccable/scripts/context.mjs @@ -904,6 +904,10 @@ async function cli() { 'reference/init.md and write PRODUCT.md first; for any other (scoped) ' + 'command against existing code, proceed using the code as context and ' + `offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, + 'NEW_WORK: No committed design context was found. If this task produces ' + + 'new design (a build from scratch, or a redesign that discards the ' + + 'current look), you MUST read reference/new-work.md before making any ' + + 'design decision. Scoped fixes to existing code do not need it.', ]; parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { @@ -922,10 +926,22 @@ async function cli() { parts.push(buildMissingTargetDirective()); } const register = extractRegister(ctx.product); - const next = register - ? `NEXT STEP: This project's register is \`${register}\`. You MUST now read \`reference/${register}.md\` before producing any design output.` - : `NEXT STEP: You MUST now read the matching register reference (\`reference/brand.md\` or \`reference/product.md\`) before producing any design output. Pick based on PRODUCT.md above.`; - parts.push(next); + // The register field survives as a family hint (brand = Persuade/Experience, + // product = Operate/Read); SKILL.md's mode section carries the essentials + // inline, so no register-file read is mandated here. What IS mandated: + // the new-work playbook when no committed design system exists yet. + if (!ctx.hasDesign) { + parts.push( + 'NEW_WORK: PRODUCT.md exists but no DESIGN.md was found. If the code ' + + 'has no committed design system either (check the project files), and ' + + 'this task produces new design or a redesign that discards the current ' + + 'look, you MUST read reference/new-work.md before making any design ' + + 'decision. Scoped fixes inside an existing design system do not need it.', + ); + } + if (register) { + parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md; \`reference/${register}.md\` has extended depth for substantial work.`); + } const platform = extractPlatform(ctx.product); const nativeRefs = platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; @@ -933,7 +949,7 @@ async function cli() { const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to the register reference.`, + `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, ); } else if (!platform) { // A `## Platform` section that names something we don't recognize (a diff --git a/.gemini/skills/impeccable/scripts/live-browser.js b/.gemini/skills/impeccable/scripts/live-browser.js index 616f1290a..b0dcaa404 100644 --- a/.gemini/skills/impeccable/scripts/live-browser.js +++ b/.gemini/skills/impeccable/scripts/live-browser.js @@ -7105,6 +7105,14 @@ } async function captureAndEmit(el, basePayload, snapshot, rect) { + const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); + + // Plain requests do not send a screenshot to the agent, so capture is + // presentation-only. Wait only for the helper to accept the event before + // starting CPU-heavy capture; this yields the browser task and prevents + // rasterization from delaying the fetch itself. + if (!hasAnnotations) await sendEvent(basePayload); + let screenshotPath; let blob; let paper; @@ -7122,7 +7130,6 @@ // are present. Without annotations the image is pure visual anchoring - // it biases the model toward the current rendering and works against the // three-distinct-directions brief. - const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); if (blob && hasAnnotations) { try { const uploadRes = await fetch( @@ -7140,7 +7147,11 @@ console.warn('[impeccable] annotation upload failed:', err); } } - sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + // Annotated requests must wait for capture + upload because the screenshot + // is semantic input. Plain requests were already dispatched above. + if (hasAnnotations) { + sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + } } // diff --git a/.github/skills/impeccable/SKILL.md b/.github/skills/impeccable/SKILL.md index 14953b932..3e57e8a38 100644 --- a/.github/skills/impeccable/SKILL.md +++ b/.github/skills/impeccable/SKILL.md @@ -1,7 +1,7 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.8 +version: 4.0.0-alpha.9 user-invocable: true argument-hint: "[craft|shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]" license: Apache 2.0 @@ -16,27 +16,13 @@ Approach every design task as the design lead at a small studio known for giving 1. Run `node .github/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's PRODUCT.md and DESIGN.md when they exist; follow what it prints, including any `UPDATE_AVAILABLE` directive (ask once, never block). If it reports `NO_PRODUCT_MD`: for `init`, `teach`, `craft`, `shape`, or wording that clearly maps to a from-scratch build flow, divert into `reference/init.md` first, **unless no user can respond** (a one-shot or automated run, or the user said not to ask): then write your own one-paragraph understanding of the product, audience, and the page's job from the brief and continue. For scoped evaluate/refine/fix requests against existing code, never divert into init; the existing code is the context. 2. If the user invoked a sub-command (`audit`, `polish`, `live`, ...), read **`reference/.md`** (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. On unattended runs, `craft` and `shape` collapse into this file's process: decide, record each decision in one line, build, self-review. 3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). -4. If the project is brand-new (no committed tokens, fonts, or brand colors found in step 3), run `node .github/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed breaks your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this step entirely when step 3 found committed brand colors: identity-preservation wins. - ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this file warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. +**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. -**Existing worlds are sacred.** When the surface already has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside it: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Treat the task as new identity work only when nothing committed exists, or when the user asks for a redesign that discards the current look. A redesign is new work: derive the concept from the subject and the brief, not from the incumbent page's structure or styling. +**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. -**Ground it in the subject.** Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. - -**Plan, self-check, build.** Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. - -**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. - -**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. - -**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. - -**Commit.** Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. - -**Calibration.** AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. +**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Not optional, not skippable under time pressure: producing new identity without it yields the generic default this skill exists to prevent. A redesign is new work; derive the concept from the subject and the brief, not from the incumbent page's structure or styling. `context.mjs` prints this directive when it detects the situation. Scoped fixes inside an existing world don't need the playbook; the craft floor below governs them. ## Craft floor @@ -54,7 +40,7 @@ Build to this floor without announcing it. The design detector (the project hook Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. The brief and the surface decide the mode. PRODUCT.md's `register` field names the family (`brand` covers Persuade and Experience; `product` covers Operate and Read), and `reference/brand.md` / `reference/product.md` carry extended depth for the two families. -**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. +**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). **Operate** (app UI, dashboards, admin, tools; design SERVES the task). A person getting something done: density, scanability, and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. diff --git a/.github/skills/impeccable/reference/new-work.md b/.github/skills/impeccable/reference/new-work.md new file mode 100644 index 000000000..352e93c18 --- /dev/null +++ b/.github/skills/impeccable/reference/new-work.md @@ -0,0 +1,37 @@ +# New identity work + +You are reading this because nothing committed exists yet (greenfield), or the user asked for a redesign that discards the current look. Either way the task is the same: invent a visual identity that could not be mistaken for anyone else's, and build it to the craft floor. SKILL.md's rules all still apply; this file is the process that produces the identity. + +## Seed + +If the project is brand-new (no committed tokens, fonts, or brand colors found in the code), run `node .github/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed exists to break your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this entirely when the code already has committed brand colors: identity-preservation wins. + +## Ground it in the subject + +Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. + +## Plan, self-check, build + +Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. + +**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. + +**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. + +**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. + +## Commit + +Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. + +## Type and imagery for new Persuade and Experience surfaces + +Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. (For Operate and Read surfaces the opposite holds: system stacks and workhorse UI faces are legitimate and often correct.) + +Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Verify stock URLs resolve before shipping them. + +## Calibration + +AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. + +Before finishing, verify against SKILL.md's craft floor and run the detector; a bold page that ships mechanical defects is not done. diff --git a/.github/skills/impeccable/scripts/context.mjs b/.github/skills/impeccable/scripts/context.mjs index 11f2aabe0..15829a157 100644 --- a/.github/skills/impeccable/scripts/context.mjs +++ b/.github/skills/impeccable/scripts/context.mjs @@ -904,6 +904,10 @@ async function cli() { 'reference/init.md and write PRODUCT.md first; for any other (scoped) ' + 'command against existing code, proceed using the code as context and ' + `offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, + 'NEW_WORK: No committed design context was found. If this task produces ' + + 'new design (a build from scratch, or a redesign that discards the ' + + 'current look), you MUST read reference/new-work.md before making any ' + + 'design decision. Scoped fixes to existing code do not need it.', ]; parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { @@ -922,10 +926,22 @@ async function cli() { parts.push(buildMissingTargetDirective()); } const register = extractRegister(ctx.product); - const next = register - ? `NEXT STEP: This project's register is \`${register}\`. You MUST now read \`reference/${register}.md\` before producing any design output.` - : `NEXT STEP: You MUST now read the matching register reference (\`reference/brand.md\` or \`reference/product.md\`) before producing any design output. Pick based on PRODUCT.md above.`; - parts.push(next); + // The register field survives as a family hint (brand = Persuade/Experience, + // product = Operate/Read); SKILL.md's mode section carries the essentials + // inline, so no register-file read is mandated here. What IS mandated: + // the new-work playbook when no committed design system exists yet. + if (!ctx.hasDesign) { + parts.push( + 'NEW_WORK: PRODUCT.md exists but no DESIGN.md was found. If the code ' + + 'has no committed design system either (check the project files), and ' + + 'this task produces new design or a redesign that discards the current ' + + 'look, you MUST read reference/new-work.md before making any design ' + + 'decision. Scoped fixes inside an existing design system do not need it.', + ); + } + if (register) { + parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md; \`reference/${register}.md\` has extended depth for substantial work.`); + } const platform = extractPlatform(ctx.product); const nativeRefs = platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; @@ -933,7 +949,7 @@ async function cli() { const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to the register reference.`, + `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, ); } else if (!platform) { // A `## Platform` section that names something we don't recognize (a diff --git a/.github/skills/impeccable/scripts/live-browser.js b/.github/skills/impeccable/scripts/live-browser.js index 616f1290a..b0dcaa404 100644 --- a/.github/skills/impeccable/scripts/live-browser.js +++ b/.github/skills/impeccable/scripts/live-browser.js @@ -7105,6 +7105,14 @@ } async function captureAndEmit(el, basePayload, snapshot, rect) { + const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); + + // Plain requests do not send a screenshot to the agent, so capture is + // presentation-only. Wait only for the helper to accept the event before + // starting CPU-heavy capture; this yields the browser task and prevents + // rasterization from delaying the fetch itself. + if (!hasAnnotations) await sendEvent(basePayload); + let screenshotPath; let blob; let paper; @@ -7122,7 +7130,6 @@ // are present. Without annotations the image is pure visual anchoring - // it biases the model toward the current rendering and works against the // three-distinct-directions brief. - const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); if (blob && hasAnnotations) { try { const uploadRes = await fetch( @@ -7140,7 +7147,11 @@ console.warn('[impeccable] annotation upload failed:', err); } } - sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + // Annotated requests must wait for capture + upload because the screenshot + // is semantic input. Plain requests were already dispatched above. + if (hasAnnotations) { + sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + } } // diff --git a/.kiro/skills/impeccable/SKILL.md b/.kiro/skills/impeccable/SKILL.md index 77c956b7e..010c29cf7 100644 --- a/.kiro/skills/impeccable/SKILL.md +++ b/.kiro/skills/impeccable/SKILL.md @@ -1,7 +1,7 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.8 +version: 4.0.0-alpha.9 license: Apache 2.0 --- @@ -14,27 +14,13 @@ Approach every design task as the design lead at a small studio known for giving 1. Run `node .kiro/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's PRODUCT.md and DESIGN.md when they exist; follow what it prints, including any `UPDATE_AVAILABLE` directive (ask once, never block). If it reports `NO_PRODUCT_MD`: for `init`, `teach`, `craft`, `shape`, or wording that clearly maps to a from-scratch build flow, divert into `reference/init.md` first, **unless no user can respond** (a one-shot or automated run, or the user said not to ask): then write your own one-paragraph understanding of the product, audience, and the page's job from the brief and continue. For scoped evaluate/refine/fix requests against existing code, never divert into init; the existing code is the context. 2. If the user invoked a sub-command (`audit`, `polish`, `live`, ...), read **`reference/.md`** (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. On unattended runs, `craft` and `shape` collapse into this file's process: decide, record each decision in one line, build, self-review. 3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). -4. If the project is brand-new (no committed tokens, fonts, or brand colors found in step 3), run `node .kiro/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed breaks your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this step entirely when step 3 found committed brand colors: identity-preservation wins. - ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this file warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. +**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. -**Existing worlds are sacred.** When the surface already has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside it: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Treat the task as new identity work only when nothing committed exists, or when the user asks for a redesign that discards the current look. A redesign is new work: derive the concept from the subject and the brief, not from the incumbent page's structure or styling. +**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. -**Ground it in the subject.** Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. - -**Plan, self-check, build.** Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. - -**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. - -**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. - -**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. - -**Commit.** Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. - -**Calibration.** AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. +**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Not optional, not skippable under time pressure: producing new identity without it yields the generic default this skill exists to prevent. A redesign is new work; derive the concept from the subject and the brief, not from the incumbent page's structure or styling. `context.mjs` prints this directive when it detects the situation. Scoped fixes inside an existing world don't need the playbook; the craft floor below governs them. ## Craft floor @@ -52,7 +38,7 @@ Build to this floor without announcing it. The design detector (the project hook Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. The brief and the surface decide the mode. PRODUCT.md's `register` field names the family (`brand` covers Persuade and Experience; `product` covers Operate and Read), and `reference/brand.md` / `reference/product.md` carry extended depth for the two families. -**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. +**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). **Operate** (app UI, dashboards, admin, tools; design SERVES the task). A person getting something done: density, scanability, and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. diff --git a/.kiro/skills/impeccable/reference/new-work.md b/.kiro/skills/impeccable/reference/new-work.md new file mode 100644 index 000000000..4f45638f3 --- /dev/null +++ b/.kiro/skills/impeccable/reference/new-work.md @@ -0,0 +1,37 @@ +# New identity work + +You are reading this because nothing committed exists yet (greenfield), or the user asked for a redesign that discards the current look. Either way the task is the same: invent a visual identity that could not be mistaken for anyone else's, and build it to the craft floor. SKILL.md's rules all still apply; this file is the process that produces the identity. + +## Seed + +If the project is brand-new (no committed tokens, fonts, or brand colors found in the code), run `node .kiro/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed exists to break your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this entirely when the code already has committed brand colors: identity-preservation wins. + +## Ground it in the subject + +Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. + +## Plan, self-check, build + +Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. + +**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. + +**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. + +**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. + +## Commit + +Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. + +## Type and imagery for new Persuade and Experience surfaces + +Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. (For Operate and Read surfaces the opposite holds: system stacks and workhorse UI faces are legitimate and often correct.) + +Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Verify stock URLs resolve before shipping them. + +## Calibration + +AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. + +Before finishing, verify against SKILL.md's craft floor and run the detector; a bold page that ships mechanical defects is not done. diff --git a/.kiro/skills/impeccable/scripts/context.mjs b/.kiro/skills/impeccable/scripts/context.mjs index 11f2aabe0..15829a157 100644 --- a/.kiro/skills/impeccable/scripts/context.mjs +++ b/.kiro/skills/impeccable/scripts/context.mjs @@ -904,6 +904,10 @@ async function cli() { 'reference/init.md and write PRODUCT.md first; for any other (scoped) ' + 'command against existing code, proceed using the code as context and ' + `offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, + 'NEW_WORK: No committed design context was found. If this task produces ' + + 'new design (a build from scratch, or a redesign that discards the ' + + 'current look), you MUST read reference/new-work.md before making any ' + + 'design decision. Scoped fixes to existing code do not need it.', ]; parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { @@ -922,10 +926,22 @@ async function cli() { parts.push(buildMissingTargetDirective()); } const register = extractRegister(ctx.product); - const next = register - ? `NEXT STEP: This project's register is \`${register}\`. You MUST now read \`reference/${register}.md\` before producing any design output.` - : `NEXT STEP: You MUST now read the matching register reference (\`reference/brand.md\` or \`reference/product.md\`) before producing any design output. Pick based on PRODUCT.md above.`; - parts.push(next); + // The register field survives as a family hint (brand = Persuade/Experience, + // product = Operate/Read); SKILL.md's mode section carries the essentials + // inline, so no register-file read is mandated here. What IS mandated: + // the new-work playbook when no committed design system exists yet. + if (!ctx.hasDesign) { + parts.push( + 'NEW_WORK: PRODUCT.md exists but no DESIGN.md was found. If the code ' + + 'has no committed design system either (check the project files), and ' + + 'this task produces new design or a redesign that discards the current ' + + 'look, you MUST read reference/new-work.md before making any design ' + + 'decision. Scoped fixes inside an existing design system do not need it.', + ); + } + if (register) { + parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md; \`reference/${register}.md\` has extended depth for substantial work.`); + } const platform = extractPlatform(ctx.product); const nativeRefs = platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; @@ -933,7 +949,7 @@ async function cli() { const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to the register reference.`, + `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, ); } else if (!platform) { // A `## Platform` section that names something we don't recognize (a diff --git a/.kiro/skills/impeccable/scripts/live-browser.js b/.kiro/skills/impeccable/scripts/live-browser.js index 616f1290a..b0dcaa404 100644 --- a/.kiro/skills/impeccable/scripts/live-browser.js +++ b/.kiro/skills/impeccable/scripts/live-browser.js @@ -7105,6 +7105,14 @@ } async function captureAndEmit(el, basePayload, snapshot, rect) { + const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); + + // Plain requests do not send a screenshot to the agent, so capture is + // presentation-only. Wait only for the helper to accept the event before + // starting CPU-heavy capture; this yields the browser task and prevents + // rasterization from delaying the fetch itself. + if (!hasAnnotations) await sendEvent(basePayload); + let screenshotPath; let blob; let paper; @@ -7122,7 +7130,6 @@ // are present. Without annotations the image is pure visual anchoring - // it biases the model toward the current rendering and works against the // three-distinct-directions brief. - const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); if (blob && hasAnnotations) { try { const uploadRes = await fetch( @@ -7140,7 +7147,11 @@ console.warn('[impeccable] annotation upload failed:', err); } } - sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + // Annotated requests must wait for capture + upload because the screenshot + // is semantic input. Plain requests were already dispatched above. + if (hasAnnotations) { + sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + } } // diff --git a/.opencode/skills/impeccable/SKILL.md b/.opencode/skills/impeccable/SKILL.md index cec8cb142..a4548ed8e 100644 --- a/.opencode/skills/impeccable/SKILL.md +++ b/.opencode/skills/impeccable/SKILL.md @@ -1,7 +1,7 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.8 +version: 4.0.0-alpha.9 user-invocable: true argument-hint: "[craft|shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]" license: Apache 2.0 @@ -19,27 +19,13 @@ Approach every design task as the design lead at a small studio known for giving 1. Run `node .opencode/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's PRODUCT.md and DESIGN.md when they exist; follow what it prints, including any `UPDATE_AVAILABLE` directive (ask once, never block). If it reports `NO_PRODUCT_MD`: for `init`, `teach`, `craft`, `shape`, or wording that clearly maps to a from-scratch build flow, divert into `reference/init.md` first, **unless no user can respond** (a one-shot or automated run, or the user said not to ask): then write your own one-paragraph understanding of the product, audience, and the page's job from the brief and continue. For scoped evaluate/refine/fix requests against existing code, never divert into init; the existing code is the context. 2. If the user invoked a sub-command (`audit`, `polish`, `live`, ...), read **`reference/.md`** (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. On unattended runs, `craft` and `shape` collapse into this file's process: decide, record each decision in one line, build, self-review. 3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). -4. If the project is brand-new (no committed tokens, fonts, or brand colors found in step 3), run `node .opencode/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed breaks your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this step entirely when step 3 found committed brand colors: identity-preservation wins. - ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this file warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. +**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. -**Existing worlds are sacred.** When the surface already has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside it: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Treat the task as new identity work only when nothing committed exists, or when the user asks for a redesign that discards the current look. A redesign is new work: derive the concept from the subject and the brief, not from the incumbent page's structure or styling. +**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. -**Ground it in the subject.** Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. - -**Plan, self-check, build.** Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. - -**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. - -**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. - -**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. - -**Commit.** Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. - -**Calibration.** AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. +**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Not optional, not skippable under time pressure: producing new identity without it yields the generic default this skill exists to prevent. A redesign is new work; derive the concept from the subject and the brief, not from the incumbent page's structure or styling. `context.mjs` prints this directive when it detects the situation. Scoped fixes inside an existing world don't need the playbook; the craft floor below governs them. ## Craft floor @@ -57,7 +43,7 @@ Build to this floor without announcing it. The design detector (the project hook Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. The brief and the surface decide the mode. PRODUCT.md's `register` field names the family (`brand` covers Persuade and Experience; `product` covers Operate and Read), and `reference/brand.md` / `reference/product.md` carry extended depth for the two families. -**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. +**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). **Operate** (app UI, dashboards, admin, tools; design SERVES the task). A person getting something done: density, scanability, and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. diff --git a/.opencode/skills/impeccable/reference/new-work.md b/.opencode/skills/impeccable/reference/new-work.md new file mode 100644 index 000000000..0c1f2bfb0 --- /dev/null +++ b/.opencode/skills/impeccable/reference/new-work.md @@ -0,0 +1,37 @@ +# New identity work + +You are reading this because nothing committed exists yet (greenfield), or the user asked for a redesign that discards the current look. Either way the task is the same: invent a visual identity that could not be mistaken for anyone else's, and build it to the craft floor. SKILL.md's rules all still apply; this file is the process that produces the identity. + +## Seed + +If the project is brand-new (no committed tokens, fonts, or brand colors found in the code), run `node .opencode/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed exists to break your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this entirely when the code already has committed brand colors: identity-preservation wins. + +## Ground it in the subject + +Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. + +## Plan, self-check, build + +Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. + +**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. + +**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. + +**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. + +## Commit + +Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. + +## Type and imagery for new Persuade and Experience surfaces + +Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. (For Operate and Read surfaces the opposite holds: system stacks and workhorse UI faces are legitimate and often correct.) + +Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Verify stock URLs resolve before shipping them. + +## Calibration + +AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. + +Before finishing, verify against SKILL.md's craft floor and run the detector; a bold page that ships mechanical defects is not done. diff --git a/.opencode/skills/impeccable/scripts/context.mjs b/.opencode/skills/impeccable/scripts/context.mjs index 11f2aabe0..15829a157 100644 --- a/.opencode/skills/impeccable/scripts/context.mjs +++ b/.opencode/skills/impeccable/scripts/context.mjs @@ -904,6 +904,10 @@ async function cli() { 'reference/init.md and write PRODUCT.md first; for any other (scoped) ' + 'command against existing code, proceed using the code as context and ' + `offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, + 'NEW_WORK: No committed design context was found. If this task produces ' + + 'new design (a build from scratch, or a redesign that discards the ' + + 'current look), you MUST read reference/new-work.md before making any ' + + 'design decision. Scoped fixes to existing code do not need it.', ]; parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { @@ -922,10 +926,22 @@ async function cli() { parts.push(buildMissingTargetDirective()); } const register = extractRegister(ctx.product); - const next = register - ? `NEXT STEP: This project's register is \`${register}\`. You MUST now read \`reference/${register}.md\` before producing any design output.` - : `NEXT STEP: You MUST now read the matching register reference (\`reference/brand.md\` or \`reference/product.md\`) before producing any design output. Pick based on PRODUCT.md above.`; - parts.push(next); + // The register field survives as a family hint (brand = Persuade/Experience, + // product = Operate/Read); SKILL.md's mode section carries the essentials + // inline, so no register-file read is mandated here. What IS mandated: + // the new-work playbook when no committed design system exists yet. + if (!ctx.hasDesign) { + parts.push( + 'NEW_WORK: PRODUCT.md exists but no DESIGN.md was found. If the code ' + + 'has no committed design system either (check the project files), and ' + + 'this task produces new design or a redesign that discards the current ' + + 'look, you MUST read reference/new-work.md before making any design ' + + 'decision. Scoped fixes inside an existing design system do not need it.', + ); + } + if (register) { + parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md; \`reference/${register}.md\` has extended depth for substantial work.`); + } const platform = extractPlatform(ctx.product); const nativeRefs = platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; @@ -933,7 +949,7 @@ async function cli() { const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to the register reference.`, + `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, ); } else if (!platform) { // A `## Platform` section that names something we don't recognize (a diff --git a/.opencode/skills/impeccable/scripts/live-browser.js b/.opencode/skills/impeccable/scripts/live-browser.js index 616f1290a..b0dcaa404 100644 --- a/.opencode/skills/impeccable/scripts/live-browser.js +++ b/.opencode/skills/impeccable/scripts/live-browser.js @@ -7105,6 +7105,14 @@ } async function captureAndEmit(el, basePayload, snapshot, rect) { + const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); + + // Plain requests do not send a screenshot to the agent, so capture is + // presentation-only. Wait only for the helper to accept the event before + // starting CPU-heavy capture; this yields the browser task and prevents + // rasterization from delaying the fetch itself. + if (!hasAnnotations) await sendEvent(basePayload); + let screenshotPath; let blob; let paper; @@ -7122,7 +7130,6 @@ // are present. Without annotations the image is pure visual anchoring - // it biases the model toward the current rendering and works against the // three-distinct-directions brief. - const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); if (blob && hasAnnotations) { try { const uploadRes = await fetch( @@ -7140,7 +7147,11 @@ console.warn('[impeccable] annotation upload failed:', err); } } - sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + // Annotated requests must wait for capture + upload because the screenshot + // is semantic input. Plain requests were already dispatched above. + if (hasAnnotations) { + sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + } } // diff --git a/.pi/skills/impeccable/SKILL.md b/.pi/skills/impeccable/SKILL.md index 795dec5f8..ac43d5ddb 100644 --- a/.pi/skills/impeccable/SKILL.md +++ b/.pi/skills/impeccable/SKILL.md @@ -1,7 +1,7 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.8 +version: 4.0.0-alpha.9 license: Apache 2.0 allowed-tools: - Bash(npx impeccable *) @@ -17,27 +17,13 @@ Approach every design task as the design lead at a small studio known for giving 1. Run `node .pi/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's PRODUCT.md and DESIGN.md when they exist; follow what it prints, including any `UPDATE_AVAILABLE` directive (ask once, never block). If it reports `NO_PRODUCT_MD`: for `init`, `teach`, `craft`, `shape`, or wording that clearly maps to a from-scratch build flow, divert into `reference/init.md` first, **unless no user can respond** (a one-shot or automated run, or the user said not to ask): then write your own one-paragraph understanding of the product, audience, and the page's job from the brief and continue. For scoped evaluate/refine/fix requests against existing code, never divert into init; the existing code is the context. 2. If the user invoked a sub-command (`audit`, `polish`, `live`, ...), read **`reference/.md`** (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. On unattended runs, `craft` and `shape` collapse into this file's process: decide, record each decision in one line, build, self-review. 3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). -4. If the project is brand-new (no committed tokens, fonts, or brand colors found in step 3), run `node .pi/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed breaks your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this step entirely when step 3 found committed brand colors: identity-preservation wins. - ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this file warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. +**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. -**Existing worlds are sacred.** When the surface already has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside it: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Treat the task as new identity work only when nothing committed exists, or when the user asks for a redesign that discards the current look. A redesign is new work: derive the concept from the subject and the brief, not from the incumbent page's structure or styling. +**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. -**Ground it in the subject.** Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. - -**Plan, self-check, build.** Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. - -**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. - -**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. - -**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. - -**Commit.** Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. - -**Calibration.** AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. +**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Not optional, not skippable under time pressure: producing new identity without it yields the generic default this skill exists to prevent. A redesign is new work; derive the concept from the subject and the brief, not from the incumbent page's structure or styling. `context.mjs` prints this directive when it detects the situation. Scoped fixes inside an existing world don't need the playbook; the craft floor below governs them. ## Craft floor @@ -55,7 +41,7 @@ Build to this floor without announcing it. The design detector (the project hook Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. The brief and the surface decide the mode. PRODUCT.md's `register` field names the family (`brand` covers Persuade and Experience; `product` covers Operate and Read), and `reference/brand.md` / `reference/product.md` carry extended depth for the two families. -**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. +**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). **Operate** (app UI, dashboards, admin, tools; design SERVES the task). A person getting something done: density, scanability, and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. diff --git a/.pi/skills/impeccable/reference/new-work.md b/.pi/skills/impeccable/reference/new-work.md new file mode 100644 index 000000000..765b149c5 --- /dev/null +++ b/.pi/skills/impeccable/reference/new-work.md @@ -0,0 +1,37 @@ +# New identity work + +You are reading this because nothing committed exists yet (greenfield), or the user asked for a redesign that discards the current look. Either way the task is the same: invent a visual identity that could not be mistaken for anyone else's, and build it to the craft floor. SKILL.md's rules all still apply; this file is the process that produces the identity. + +## Seed + +If the project is brand-new (no committed tokens, fonts, or brand colors found in the code), run `node .pi/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed exists to break your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this entirely when the code already has committed brand colors: identity-preservation wins. + +## Ground it in the subject + +Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. + +## Plan, self-check, build + +Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. + +**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. + +**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. + +**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. + +## Commit + +Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. + +## Type and imagery for new Persuade and Experience surfaces + +Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. (For Operate and Read surfaces the opposite holds: system stacks and workhorse UI faces are legitimate and often correct.) + +Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Verify stock URLs resolve before shipping them. + +## Calibration + +AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. + +Before finishing, verify against SKILL.md's craft floor and run the detector; a bold page that ships mechanical defects is not done. diff --git a/.pi/skills/impeccable/scripts/context.mjs b/.pi/skills/impeccable/scripts/context.mjs index 11f2aabe0..15829a157 100644 --- a/.pi/skills/impeccable/scripts/context.mjs +++ b/.pi/skills/impeccable/scripts/context.mjs @@ -904,6 +904,10 @@ async function cli() { 'reference/init.md and write PRODUCT.md first; for any other (scoped) ' + 'command against existing code, proceed using the code as context and ' + `offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, + 'NEW_WORK: No committed design context was found. If this task produces ' + + 'new design (a build from scratch, or a redesign that discards the ' + + 'current look), you MUST read reference/new-work.md before making any ' + + 'design decision. Scoped fixes to existing code do not need it.', ]; parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { @@ -922,10 +926,22 @@ async function cli() { parts.push(buildMissingTargetDirective()); } const register = extractRegister(ctx.product); - const next = register - ? `NEXT STEP: This project's register is \`${register}\`. You MUST now read \`reference/${register}.md\` before producing any design output.` - : `NEXT STEP: You MUST now read the matching register reference (\`reference/brand.md\` or \`reference/product.md\`) before producing any design output. Pick based on PRODUCT.md above.`; - parts.push(next); + // The register field survives as a family hint (brand = Persuade/Experience, + // product = Operate/Read); SKILL.md's mode section carries the essentials + // inline, so no register-file read is mandated here. What IS mandated: + // the new-work playbook when no committed design system exists yet. + if (!ctx.hasDesign) { + parts.push( + 'NEW_WORK: PRODUCT.md exists but no DESIGN.md was found. If the code ' + + 'has no committed design system either (check the project files), and ' + + 'this task produces new design or a redesign that discards the current ' + + 'look, you MUST read reference/new-work.md before making any design ' + + 'decision. Scoped fixes inside an existing design system do not need it.', + ); + } + if (register) { + parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md; \`reference/${register}.md\` has extended depth for substantial work.`); + } const platform = extractPlatform(ctx.product); const nativeRefs = platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; @@ -933,7 +949,7 @@ async function cli() { const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to the register reference.`, + `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, ); } else if (!platform) { // A `## Platform` section that names something we don't recognize (a diff --git a/.pi/skills/impeccable/scripts/live-browser.js b/.pi/skills/impeccable/scripts/live-browser.js index 616f1290a..b0dcaa404 100644 --- a/.pi/skills/impeccable/scripts/live-browser.js +++ b/.pi/skills/impeccable/scripts/live-browser.js @@ -7105,6 +7105,14 @@ } async function captureAndEmit(el, basePayload, snapshot, rect) { + const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); + + // Plain requests do not send a screenshot to the agent, so capture is + // presentation-only. Wait only for the helper to accept the event before + // starting CPU-heavy capture; this yields the browser task and prevents + // rasterization from delaying the fetch itself. + if (!hasAnnotations) await sendEvent(basePayload); + let screenshotPath; let blob; let paper; @@ -7122,7 +7130,6 @@ // are present. Without annotations the image is pure visual anchoring - // it biases the model toward the current rendering and works against the // three-distinct-directions brief. - const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); if (blob && hasAnnotations) { try { const uploadRes = await fetch( @@ -7140,7 +7147,11 @@ console.warn('[impeccable] annotation upload failed:', err); } } - sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + // Annotated requests must wait for capture + upload because the screenshot + // is semantic input. Plain requests were already dispatched above. + if (hasAnnotations) { + sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + } } // diff --git a/.qoder/skills/impeccable/SKILL.md b/.qoder/skills/impeccable/SKILL.md index 6ed61eed6..a589b9dc2 100644 --- a/.qoder/skills/impeccable/SKILL.md +++ b/.qoder/skills/impeccable/SKILL.md @@ -1,7 +1,7 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.8 +version: 4.0.0-alpha.9 user-invocable: true argument-hint: "[craft|shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]" license: Apache 2.0 @@ -19,27 +19,13 @@ Approach every design task as the design lead at a small studio known for giving 1. Run `node .qoder/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's PRODUCT.md and DESIGN.md when they exist; follow what it prints, including any `UPDATE_AVAILABLE` directive (ask once, never block). If it reports `NO_PRODUCT_MD`: for `init`, `teach`, `craft`, `shape`, or wording that clearly maps to a from-scratch build flow, divert into `reference/init.md` first, **unless no user can respond** (a one-shot or automated run, or the user said not to ask): then write your own one-paragraph understanding of the product, audience, and the page's job from the brief and continue. For scoped evaluate/refine/fix requests against existing code, never divert into init; the existing code is the context. 2. If the user invoked a sub-command (`audit`, `polish`, `live`, ...), read **`reference/.md`** (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. On unattended runs, `craft` and `shape` collapse into this file's process: decide, record each decision in one line, build, self-review. 3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). -4. If the project is brand-new (no committed tokens, fonts, or brand colors found in step 3), run `node .qoder/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed breaks your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this step entirely when step 3 found committed brand colors: identity-preservation wins. - ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this file warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. +**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. -**Existing worlds are sacred.** When the surface already has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside it: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Treat the task as new identity work only when nothing committed exists, or when the user asks for a redesign that discards the current look. A redesign is new work: derive the concept from the subject and the brief, not from the incumbent page's structure or styling. +**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. -**Ground it in the subject.** Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. - -**Plan, self-check, build.** Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. - -**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. - -**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. - -**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. - -**Commit.** Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. - -**Calibration.** AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. +**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Not optional, not skippable under time pressure: producing new identity without it yields the generic default this skill exists to prevent. A redesign is new work; derive the concept from the subject and the brief, not from the incumbent page's structure or styling. `context.mjs` prints this directive when it detects the situation. Scoped fixes inside an existing world don't need the playbook; the craft floor below governs them. ## Craft floor @@ -57,7 +43,7 @@ Build to this floor without announcing it. The design detector (the project hook Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. The brief and the surface decide the mode. PRODUCT.md's `register` field names the family (`brand` covers Persuade and Experience; `product` covers Operate and Read), and `reference/brand.md` / `reference/product.md` carry extended depth for the two families. -**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. +**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). **Operate** (app UI, dashboards, admin, tools; design SERVES the task). A person getting something done: density, scanability, and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. diff --git a/.qoder/skills/impeccable/reference/new-work.md b/.qoder/skills/impeccable/reference/new-work.md new file mode 100644 index 000000000..07868de22 --- /dev/null +++ b/.qoder/skills/impeccable/reference/new-work.md @@ -0,0 +1,37 @@ +# New identity work + +You are reading this because nothing committed exists yet (greenfield), or the user asked for a redesign that discards the current look. Either way the task is the same: invent a visual identity that could not be mistaken for anyone else's, and build it to the craft floor. SKILL.md's rules all still apply; this file is the process that produces the identity. + +## Seed + +If the project is brand-new (no committed tokens, fonts, or brand colors found in the code), run `node .qoder/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed exists to break your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this entirely when the code already has committed brand colors: identity-preservation wins. + +## Ground it in the subject + +Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. + +## Plan, self-check, build + +Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. + +**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. + +**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. + +**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. + +## Commit + +Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. + +## Type and imagery for new Persuade and Experience surfaces + +Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. (For Operate and Read surfaces the opposite holds: system stacks and workhorse UI faces are legitimate and often correct.) + +Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Verify stock URLs resolve before shipping them. + +## Calibration + +AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. + +Before finishing, verify against SKILL.md's craft floor and run the detector; a bold page that ships mechanical defects is not done. diff --git a/.qoder/skills/impeccable/scripts/context.mjs b/.qoder/skills/impeccable/scripts/context.mjs index 11f2aabe0..15829a157 100644 --- a/.qoder/skills/impeccable/scripts/context.mjs +++ b/.qoder/skills/impeccable/scripts/context.mjs @@ -904,6 +904,10 @@ async function cli() { 'reference/init.md and write PRODUCT.md first; for any other (scoped) ' + 'command against existing code, proceed using the code as context and ' + `offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, + 'NEW_WORK: No committed design context was found. If this task produces ' + + 'new design (a build from scratch, or a redesign that discards the ' + + 'current look), you MUST read reference/new-work.md before making any ' + + 'design decision. Scoped fixes to existing code do not need it.', ]; parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { @@ -922,10 +926,22 @@ async function cli() { parts.push(buildMissingTargetDirective()); } const register = extractRegister(ctx.product); - const next = register - ? `NEXT STEP: This project's register is \`${register}\`. You MUST now read \`reference/${register}.md\` before producing any design output.` - : `NEXT STEP: You MUST now read the matching register reference (\`reference/brand.md\` or \`reference/product.md\`) before producing any design output. Pick based on PRODUCT.md above.`; - parts.push(next); + // The register field survives as a family hint (brand = Persuade/Experience, + // product = Operate/Read); SKILL.md's mode section carries the essentials + // inline, so no register-file read is mandated here. What IS mandated: + // the new-work playbook when no committed design system exists yet. + if (!ctx.hasDesign) { + parts.push( + 'NEW_WORK: PRODUCT.md exists but no DESIGN.md was found. If the code ' + + 'has no committed design system either (check the project files), and ' + + 'this task produces new design or a redesign that discards the current ' + + 'look, you MUST read reference/new-work.md before making any design ' + + 'decision. Scoped fixes inside an existing design system do not need it.', + ); + } + if (register) { + parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md; \`reference/${register}.md\` has extended depth for substantial work.`); + } const platform = extractPlatform(ctx.product); const nativeRefs = platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; @@ -933,7 +949,7 @@ async function cli() { const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to the register reference.`, + `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, ); } else if (!platform) { // A `## Platform` section that names something we don't recognize (a diff --git a/.qoder/skills/impeccable/scripts/live-browser.js b/.qoder/skills/impeccable/scripts/live-browser.js index 616f1290a..b0dcaa404 100644 --- a/.qoder/skills/impeccable/scripts/live-browser.js +++ b/.qoder/skills/impeccable/scripts/live-browser.js @@ -7105,6 +7105,14 @@ } async function captureAndEmit(el, basePayload, snapshot, rect) { + const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); + + // Plain requests do not send a screenshot to the agent, so capture is + // presentation-only. Wait only for the helper to accept the event before + // starting CPU-heavy capture; this yields the browser task and prevents + // rasterization from delaying the fetch itself. + if (!hasAnnotations) await sendEvent(basePayload); + let screenshotPath; let blob; let paper; @@ -7122,7 +7130,6 @@ // are present. Without annotations the image is pure visual anchoring - // it biases the model toward the current rendering and works against the // three-distinct-directions brief. - const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); if (blob && hasAnnotations) { try { const uploadRes = await fetch( @@ -7140,7 +7147,11 @@ console.warn('[impeccable] annotation upload failed:', err); } } - sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + // Annotated requests must wait for capture + upload because the screenshot + // is semantic input. Plain requests were already dispatched above. + if (hasAnnotations) { + sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + } } // diff --git a/.rovodev/skills/impeccable/SKILL.md b/.rovodev/skills/impeccable/SKILL.md index 80de9007a..6bdedf39e 100644 --- a/.rovodev/skills/impeccable/SKILL.md +++ b/.rovodev/skills/impeccable/SKILL.md @@ -1,7 +1,7 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.8 +version: 4.0.0-alpha.9 user-invocable: true argument-hint: "[craft|shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]" license: Apache 2.0 @@ -19,27 +19,13 @@ Approach every design task as the design lead at a small studio known for giving 1. Run `node .rovodev/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's PRODUCT.md and DESIGN.md when they exist; follow what it prints, including any `UPDATE_AVAILABLE` directive (ask once, never block). If it reports `NO_PRODUCT_MD`: for `init`, `teach`, `craft`, `shape`, or wording that clearly maps to a from-scratch build flow, divert into `reference/init.md` first, **unless no user can respond** (a one-shot or automated run, or the user said not to ask): then write your own one-paragraph understanding of the product, audience, and the page's job from the brief and continue. For scoped evaluate/refine/fix requests against existing code, never divert into init; the existing code is the context. 2. If the user invoked a sub-command (`audit`, `polish`, `live`, ...), read **`reference/.md`** (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. On unattended runs, `craft` and `shape` collapse into this file's process: decide, record each decision in one line, build, self-review. 3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). -4. If the project is brand-new (no committed tokens, fonts, or brand colors found in step 3), run `node .rovodev/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed breaks your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this step entirely when step 3 found committed brand colors: identity-preservation wins. - ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this file warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. +**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. -**Existing worlds are sacred.** When the surface already has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside it: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Treat the task as new identity work only when nothing committed exists, or when the user asks for a redesign that discards the current look. A redesign is new work: derive the concept from the subject and the brief, not from the incumbent page's structure or styling. +**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. -**Ground it in the subject.** Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. - -**Plan, self-check, build.** Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. - -**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. - -**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. - -**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. - -**Commit.** Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. - -**Calibration.** AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. +**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Not optional, not skippable under time pressure: producing new identity without it yields the generic default this skill exists to prevent. A redesign is new work; derive the concept from the subject and the brief, not from the incumbent page's structure or styling. `context.mjs` prints this directive when it detects the situation. Scoped fixes inside an existing world don't need the playbook; the craft floor below governs them. ## Craft floor @@ -57,7 +43,7 @@ Build to this floor without announcing it. The design detector (the project hook Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. The brief and the surface decide the mode. PRODUCT.md's `register` field names the family (`brand` covers Persuade and Experience; `product` covers Operate and Read), and `reference/brand.md` / `reference/product.md` carry extended depth for the two families. -**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. +**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). **Operate** (app UI, dashboards, admin, tools; design SERVES the task). A person getting something done: density, scanability, and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. diff --git a/.rovodev/skills/impeccable/reference/new-work.md b/.rovodev/skills/impeccable/reference/new-work.md new file mode 100644 index 000000000..3a30a5c13 --- /dev/null +++ b/.rovodev/skills/impeccable/reference/new-work.md @@ -0,0 +1,37 @@ +# New identity work + +You are reading this because nothing committed exists yet (greenfield), or the user asked for a redesign that discards the current look. Either way the task is the same: invent a visual identity that could not be mistaken for anyone else's, and build it to the craft floor. SKILL.md's rules all still apply; this file is the process that produces the identity. + +## Seed + +If the project is brand-new (no committed tokens, fonts, or brand colors found in the code), run `node .rovodev/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed exists to break your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this entirely when the code already has committed brand colors: identity-preservation wins. + +## Ground it in the subject + +Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. + +## Plan, self-check, build + +Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. + +**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. + +**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. + +**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. + +## Commit + +Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. + +## Type and imagery for new Persuade and Experience surfaces + +Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. (For Operate and Read surfaces the opposite holds: system stacks and workhorse UI faces are legitimate and often correct.) + +Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Verify stock URLs resolve before shipping them. + +## Calibration + +AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. + +Before finishing, verify against SKILL.md's craft floor and run the detector; a bold page that ships mechanical defects is not done. diff --git a/.rovodev/skills/impeccable/scripts/context.mjs b/.rovodev/skills/impeccable/scripts/context.mjs index 11f2aabe0..15829a157 100644 --- a/.rovodev/skills/impeccable/scripts/context.mjs +++ b/.rovodev/skills/impeccable/scripts/context.mjs @@ -904,6 +904,10 @@ async function cli() { 'reference/init.md and write PRODUCT.md first; for any other (scoped) ' + 'command against existing code, proceed using the code as context and ' + `offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, + 'NEW_WORK: No committed design context was found. If this task produces ' + + 'new design (a build from scratch, or a redesign that discards the ' + + 'current look), you MUST read reference/new-work.md before making any ' + + 'design decision. Scoped fixes to existing code do not need it.', ]; parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { @@ -922,10 +926,22 @@ async function cli() { parts.push(buildMissingTargetDirective()); } const register = extractRegister(ctx.product); - const next = register - ? `NEXT STEP: This project's register is \`${register}\`. You MUST now read \`reference/${register}.md\` before producing any design output.` - : `NEXT STEP: You MUST now read the matching register reference (\`reference/brand.md\` or \`reference/product.md\`) before producing any design output. Pick based on PRODUCT.md above.`; - parts.push(next); + // The register field survives as a family hint (brand = Persuade/Experience, + // product = Operate/Read); SKILL.md's mode section carries the essentials + // inline, so no register-file read is mandated here. What IS mandated: + // the new-work playbook when no committed design system exists yet. + if (!ctx.hasDesign) { + parts.push( + 'NEW_WORK: PRODUCT.md exists but no DESIGN.md was found. If the code ' + + 'has no committed design system either (check the project files), and ' + + 'this task produces new design or a redesign that discards the current ' + + 'look, you MUST read reference/new-work.md before making any design ' + + 'decision. Scoped fixes inside an existing design system do not need it.', + ); + } + if (register) { + parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md; \`reference/${register}.md\` has extended depth for substantial work.`); + } const platform = extractPlatform(ctx.product); const nativeRefs = platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; @@ -933,7 +949,7 @@ async function cli() { const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to the register reference.`, + `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, ); } else if (!platform) { // A `## Platform` section that names something we don't recognize (a diff --git a/.rovodev/skills/impeccable/scripts/live-browser.js b/.rovodev/skills/impeccable/scripts/live-browser.js index 616f1290a..b0dcaa404 100644 --- a/.rovodev/skills/impeccable/scripts/live-browser.js +++ b/.rovodev/skills/impeccable/scripts/live-browser.js @@ -7105,6 +7105,14 @@ } async function captureAndEmit(el, basePayload, snapshot, rect) { + const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); + + // Plain requests do not send a screenshot to the agent, so capture is + // presentation-only. Wait only for the helper to accept the event before + // starting CPU-heavy capture; this yields the browser task and prevents + // rasterization from delaying the fetch itself. + if (!hasAnnotations) await sendEvent(basePayload); + let screenshotPath; let blob; let paper; @@ -7122,7 +7130,6 @@ // are present. Without annotations the image is pure visual anchoring - // it biases the model toward the current rendering and works against the // three-distinct-directions brief. - const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); if (blob && hasAnnotations) { try { const uploadRes = await fetch( @@ -7140,7 +7147,11 @@ console.warn('[impeccable] annotation upload failed:', err); } } - sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + // Annotated requests must wait for capture + upload because the screenshot + // is semantic input. Plain requests were already dispatched above. + if (hasAnnotations) { + sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + } } // diff --git a/.trae-cn/skills/impeccable/SKILL.md b/.trae-cn/skills/impeccable/SKILL.md index 6f37fdf35..fdb7548c7 100644 --- a/.trae-cn/skills/impeccable/SKILL.md +++ b/.trae-cn/skills/impeccable/SKILL.md @@ -1,7 +1,7 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.8 +version: 4.0.0-alpha.9 user-invocable: true argument-hint: "[craft|shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]" license: Apache 2.0 @@ -16,27 +16,13 @@ Approach every design task as the design lead at a small studio known for giving 1. Run `node .trae-cn/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's PRODUCT.md and DESIGN.md when they exist; follow what it prints, including any `UPDATE_AVAILABLE` directive (ask once, never block). If it reports `NO_PRODUCT_MD`: for `init`, `teach`, `craft`, `shape`, or wording that clearly maps to a from-scratch build flow, divert into `reference/init.md` first, **unless no user can respond** (a one-shot or automated run, or the user said not to ask): then write your own one-paragraph understanding of the product, audience, and the page's job from the brief and continue. For scoped evaluate/refine/fix requests against existing code, never divert into init; the existing code is the context. 2. If the user invoked a sub-command (`audit`, `polish`, `live`, ...), read **`reference/.md`** (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. On unattended runs, `craft` and `shape` collapse into this file's process: decide, record each decision in one line, build, self-review. 3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). -4. If the project is brand-new (no committed tokens, fonts, or brand colors found in step 3), run `node .trae-cn/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed breaks your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this step entirely when step 3 found committed brand colors: identity-preservation wins. - ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this file warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. +**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. -**Existing worlds are sacred.** When the surface already has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside it: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Treat the task as new identity work only when nothing committed exists, or when the user asks for a redesign that discards the current look. A redesign is new work: derive the concept from the subject and the brief, not from the incumbent page's structure or styling. +**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. -**Ground it in the subject.** Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. - -**Plan, self-check, build.** Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. - -**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. - -**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. - -**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. - -**Commit.** Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. - -**Calibration.** AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. +**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Not optional, not skippable under time pressure: producing new identity without it yields the generic default this skill exists to prevent. A redesign is new work; derive the concept from the subject and the brief, not from the incumbent page's structure or styling. `context.mjs` prints this directive when it detects the situation. Scoped fixes inside an existing world don't need the playbook; the craft floor below governs them. ## Craft floor @@ -54,7 +40,7 @@ Build to this floor without announcing it. The design detector (the project hook Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. The brief and the surface decide the mode. PRODUCT.md's `register` field names the family (`brand` covers Persuade and Experience; `product` covers Operate and Read), and `reference/brand.md` / `reference/product.md` carry extended depth for the two families. -**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. +**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). **Operate** (app UI, dashboards, admin, tools; design SERVES the task). A person getting something done: density, scanability, and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. diff --git a/.trae-cn/skills/impeccable/reference/new-work.md b/.trae-cn/skills/impeccable/reference/new-work.md new file mode 100644 index 000000000..2a4d0078e --- /dev/null +++ b/.trae-cn/skills/impeccable/reference/new-work.md @@ -0,0 +1,37 @@ +# New identity work + +You are reading this because nothing committed exists yet (greenfield), or the user asked for a redesign that discards the current look. Either way the task is the same: invent a visual identity that could not be mistaken for anyone else's, and build it to the craft floor. SKILL.md's rules all still apply; this file is the process that produces the identity. + +## Seed + +If the project is brand-new (no committed tokens, fonts, or brand colors found in the code), run `node .trae-cn/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed exists to break your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this entirely when the code already has committed brand colors: identity-preservation wins. + +## Ground it in the subject + +Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. + +## Plan, self-check, build + +Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. + +**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. + +**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. + +**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. + +## Commit + +Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. + +## Type and imagery for new Persuade and Experience surfaces + +Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. (For Operate and Read surfaces the opposite holds: system stacks and workhorse UI faces are legitimate and often correct.) + +Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Verify stock URLs resolve before shipping them. + +## Calibration + +AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. + +Before finishing, verify against SKILL.md's craft floor and run the detector; a bold page that ships mechanical defects is not done. diff --git a/.trae-cn/skills/impeccable/scripts/context.mjs b/.trae-cn/skills/impeccable/scripts/context.mjs index 11f2aabe0..15829a157 100644 --- a/.trae-cn/skills/impeccable/scripts/context.mjs +++ b/.trae-cn/skills/impeccable/scripts/context.mjs @@ -904,6 +904,10 @@ async function cli() { 'reference/init.md and write PRODUCT.md first; for any other (scoped) ' + 'command against existing code, proceed using the code as context and ' + `offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, + 'NEW_WORK: No committed design context was found. If this task produces ' + + 'new design (a build from scratch, or a redesign that discards the ' + + 'current look), you MUST read reference/new-work.md before making any ' + + 'design decision. Scoped fixes to existing code do not need it.', ]; parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { @@ -922,10 +926,22 @@ async function cli() { parts.push(buildMissingTargetDirective()); } const register = extractRegister(ctx.product); - const next = register - ? `NEXT STEP: This project's register is \`${register}\`. You MUST now read \`reference/${register}.md\` before producing any design output.` - : `NEXT STEP: You MUST now read the matching register reference (\`reference/brand.md\` or \`reference/product.md\`) before producing any design output. Pick based on PRODUCT.md above.`; - parts.push(next); + // The register field survives as a family hint (brand = Persuade/Experience, + // product = Operate/Read); SKILL.md's mode section carries the essentials + // inline, so no register-file read is mandated here. What IS mandated: + // the new-work playbook when no committed design system exists yet. + if (!ctx.hasDesign) { + parts.push( + 'NEW_WORK: PRODUCT.md exists but no DESIGN.md was found. If the code ' + + 'has no committed design system either (check the project files), and ' + + 'this task produces new design or a redesign that discards the current ' + + 'look, you MUST read reference/new-work.md before making any design ' + + 'decision. Scoped fixes inside an existing design system do not need it.', + ); + } + if (register) { + parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md; \`reference/${register}.md\` has extended depth for substantial work.`); + } const platform = extractPlatform(ctx.product); const nativeRefs = platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; @@ -933,7 +949,7 @@ async function cli() { const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to the register reference.`, + `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, ); } else if (!platform) { // A `## Platform` section that names something we don't recognize (a diff --git a/.trae-cn/skills/impeccable/scripts/live-browser.js b/.trae-cn/skills/impeccable/scripts/live-browser.js index 616f1290a..b0dcaa404 100644 --- a/.trae-cn/skills/impeccable/scripts/live-browser.js +++ b/.trae-cn/skills/impeccable/scripts/live-browser.js @@ -7105,6 +7105,14 @@ } async function captureAndEmit(el, basePayload, snapshot, rect) { + const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); + + // Plain requests do not send a screenshot to the agent, so capture is + // presentation-only. Wait only for the helper to accept the event before + // starting CPU-heavy capture; this yields the browser task and prevents + // rasterization from delaying the fetch itself. + if (!hasAnnotations) await sendEvent(basePayload); + let screenshotPath; let blob; let paper; @@ -7122,7 +7130,6 @@ // are present. Without annotations the image is pure visual anchoring - // it biases the model toward the current rendering and works against the // three-distinct-directions brief. - const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); if (blob && hasAnnotations) { try { const uploadRes = await fetch( @@ -7140,7 +7147,11 @@ console.warn('[impeccable] annotation upload failed:', err); } } - sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + // Annotated requests must wait for capture + upload because the screenshot + // is semantic input. Plain requests were already dispatched above. + if (hasAnnotations) { + sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + } } // diff --git a/.trae/skills/impeccable/SKILL.md b/.trae/skills/impeccable/SKILL.md index 9d4d54bbe..06d1e9e9d 100644 --- a/.trae/skills/impeccable/SKILL.md +++ b/.trae/skills/impeccable/SKILL.md @@ -1,7 +1,7 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.8 +version: 4.0.0-alpha.9 user-invocable: true argument-hint: "[craft|shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]" license: Apache 2.0 @@ -16,27 +16,13 @@ Approach every design task as the design lead at a small studio known for giving 1. Run `node .trae/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's PRODUCT.md and DESIGN.md when they exist; follow what it prints, including any `UPDATE_AVAILABLE` directive (ask once, never block). If it reports `NO_PRODUCT_MD`: for `init`, `teach`, `craft`, `shape`, or wording that clearly maps to a from-scratch build flow, divert into `reference/init.md` first, **unless no user can respond** (a one-shot or automated run, or the user said not to ask): then write your own one-paragraph understanding of the product, audience, and the page's job from the brief and continue. For scoped evaluate/refine/fix requests against existing code, never divert into init; the existing code is the context. 2. If the user invoked a sub-command (`audit`, `polish`, `live`, ...), read **`reference/.md`** (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. On unattended runs, `craft` and `shape` collapse into this file's process: decide, record each decision in one line, build, self-review. 3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). -4. If the project is brand-new (no committed tokens, fonts, or brand colors found in step 3), run `node .trae/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed breaks your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this step entirely when step 3 found committed brand colors: identity-preservation wins. - ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this file warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. +**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. -**Existing worlds are sacred.** When the surface already has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside it: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Treat the task as new identity work only when nothing committed exists, or when the user asks for a redesign that discards the current look. A redesign is new work: derive the concept from the subject and the brief, not from the incumbent page's structure or styling. +**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. -**Ground it in the subject.** Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. - -**Plan, self-check, build.** Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. - -**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. - -**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. - -**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. - -**Commit.** Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. - -**Calibration.** AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. +**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Not optional, not skippable under time pressure: producing new identity without it yields the generic default this skill exists to prevent. A redesign is new work; derive the concept from the subject and the brief, not from the incumbent page's structure or styling. `context.mjs` prints this directive when it detects the situation. Scoped fixes inside an existing world don't need the playbook; the craft floor below governs them. ## Craft floor @@ -54,7 +40,7 @@ Build to this floor without announcing it. The design detector (the project hook Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. The brief and the surface decide the mode. PRODUCT.md's `register` field names the family (`brand` covers Persuade and Experience; `product` covers Operate and Read), and `reference/brand.md` / `reference/product.md` carry extended depth for the two families. -**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. +**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). **Operate** (app UI, dashboards, admin, tools; design SERVES the task). A person getting something done: density, scanability, and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. diff --git a/.trae/skills/impeccable/reference/new-work.md b/.trae/skills/impeccable/reference/new-work.md new file mode 100644 index 000000000..201d14898 --- /dev/null +++ b/.trae/skills/impeccable/reference/new-work.md @@ -0,0 +1,37 @@ +# New identity work + +You are reading this because nothing committed exists yet (greenfield), or the user asked for a redesign that discards the current look. Either way the task is the same: invent a visual identity that could not be mistaken for anyone else's, and build it to the craft floor. SKILL.md's rules all still apply; this file is the process that produces the identity. + +## Seed + +If the project is brand-new (no committed tokens, fonts, or brand colors found in the code), run `node .trae/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed exists to break your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this entirely when the code already has committed brand colors: identity-preservation wins. + +## Ground it in the subject + +Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. + +## Plan, self-check, build + +Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. + +**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. + +**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. + +**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. + +## Commit + +Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. + +## Type and imagery for new Persuade and Experience surfaces + +Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. (For Operate and Read surfaces the opposite holds: system stacks and workhorse UI faces are legitimate and often correct.) + +Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Verify stock URLs resolve before shipping them. + +## Calibration + +AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. + +Before finishing, verify against SKILL.md's craft floor and run the detector; a bold page that ships mechanical defects is not done. diff --git a/.trae/skills/impeccable/scripts/context.mjs b/.trae/skills/impeccable/scripts/context.mjs index 11f2aabe0..15829a157 100644 --- a/.trae/skills/impeccable/scripts/context.mjs +++ b/.trae/skills/impeccable/scripts/context.mjs @@ -904,6 +904,10 @@ async function cli() { 'reference/init.md and write PRODUCT.md first; for any other (scoped) ' + 'command against existing code, proceed using the code as context and ' + `offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, + 'NEW_WORK: No committed design context was found. If this task produces ' + + 'new design (a build from scratch, or a redesign that discards the ' + + 'current look), you MUST read reference/new-work.md before making any ' + + 'design decision. Scoped fixes to existing code do not need it.', ]; parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { @@ -922,10 +926,22 @@ async function cli() { parts.push(buildMissingTargetDirective()); } const register = extractRegister(ctx.product); - const next = register - ? `NEXT STEP: This project's register is \`${register}\`. You MUST now read \`reference/${register}.md\` before producing any design output.` - : `NEXT STEP: You MUST now read the matching register reference (\`reference/brand.md\` or \`reference/product.md\`) before producing any design output. Pick based on PRODUCT.md above.`; - parts.push(next); + // The register field survives as a family hint (brand = Persuade/Experience, + // product = Operate/Read); SKILL.md's mode section carries the essentials + // inline, so no register-file read is mandated here. What IS mandated: + // the new-work playbook when no committed design system exists yet. + if (!ctx.hasDesign) { + parts.push( + 'NEW_WORK: PRODUCT.md exists but no DESIGN.md was found. If the code ' + + 'has no committed design system either (check the project files), and ' + + 'this task produces new design or a redesign that discards the current ' + + 'look, you MUST read reference/new-work.md before making any design ' + + 'decision. Scoped fixes inside an existing design system do not need it.', + ); + } + if (register) { + parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md; \`reference/${register}.md\` has extended depth for substantial work.`); + } const platform = extractPlatform(ctx.product); const nativeRefs = platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; @@ -933,7 +949,7 @@ async function cli() { const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to the register reference.`, + `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, ); } else if (!platform) { // A `## Platform` section that names something we don't recognize (a diff --git a/.trae/skills/impeccable/scripts/live-browser.js b/.trae/skills/impeccable/scripts/live-browser.js index 616f1290a..b0dcaa404 100644 --- a/.trae/skills/impeccable/scripts/live-browser.js +++ b/.trae/skills/impeccable/scripts/live-browser.js @@ -7105,6 +7105,14 @@ } async function captureAndEmit(el, basePayload, snapshot, rect) { + const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); + + // Plain requests do not send a screenshot to the agent, so capture is + // presentation-only. Wait only for the helper to accept the event before + // starting CPU-heavy capture; this yields the browser task and prevents + // rasterization from delaying the fetch itself. + if (!hasAnnotations) await sendEvent(basePayload); + let screenshotPath; let blob; let paper; @@ -7122,7 +7130,6 @@ // are present. Without annotations the image is pure visual anchoring - // it biases the model toward the current rendering and works against the // three-distinct-directions brief. - const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); if (blob && hasAnnotations) { try { const uploadRes = await fetch( @@ -7140,7 +7147,11 @@ console.warn('[impeccable] annotation upload failed:', err); } } - sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + // Annotated requests must wait for capture + upload because the screenshot + // is semantic input. Plain requests were already dispatched above. + if (hasAnnotations) { + sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + } } // diff --git a/docs/live-performance.md b/docs/live-performance.md index 09e0f88aa..1c282c277 100644 --- a/docs/live-performance.md +++ b/docs/live-performance.md @@ -1,4 +1,4 @@ -# Impeccable Live performance baseline +# Impeccable Live performance baseline and first optimization ## Goal @@ -8,30 +8,36 @@ Measure the latency Impeccable controls in the Pick → Go → first usable vari `bun run bench:live` reuses the Live runtime E2E fixture system. It stages a real framework project, starts the helper and framework dev servers, opens Chromium, drives the picker, and records monotonic boundaries for: -1. Go → generate POST begins (`browserPreparationMs`) -2. generate POST → agent poll receives the event (`serverPickupMs`) -3. deterministic source scaffold (`scaffoldMs`) -4. agent generation (`generationMs`) -5. source write (`writeMs`) -6. write → first variant in the DOM (`writeToFirstVariantMs`) -7. first variant → all variants/cycling (`deliveryGapMs`) +1. Playwright starts Go → generate POST begins (`browserPreparationMs`, includes automation actionability) +2. browser Go handler → generate fetch begins (`browserDispatchMs`) +3. Playwright actionability before the Go handler (`automationClickMs`) +4. generate POST → agent poll receives the event (`serverPickupMs`) +5. deterministic source scaffold (`scaffoldMs`) +6. agent generation (`generationMs`) +7. source write (`writeMs`) +8. write → first variant in the DOM (`writeToFirstVariantMs`) +9. first variant → all variants/cycling (`deliveryGapMs`) The deterministic agent measures Impeccable’s fixed floor. An LLM-backed run uses the same orchestration seam, but remains opt-in because it sends staged fixture source to an external provider. -## Baseline, July 11 2026 +## Baseline and result, July 11 2026 Fixture: `vite8-react-plain`. Browser: local headless Chromium. Three variants. Warm interaction loop. -| Metric | Plain, median | Annotated, median | -|---|---:|---:| -| Go → first variant | 916 ms | 431 ms | -| Browser preparation | 828 ms | 335 ms | -| Server pickup | 0.8 ms | 0.4 ms | -| Scaffold | 41 ms | 41 ms | -| Write → first variant | 47 ms | 54 ms | -| First → all variants | 1.3 ms | 1.3 ms | +| Metric | Before, plain | After, plain | Annotated control | +|---|---:|---:|---:| +| Go → first variant | 916 ms | 414 ms | 423 ms | +| Browser preparation, including Playwright | 828 ms | 312 ms | 350 ms | +| Browser handler → generate fetch | — | 2.2 ms | 46.7 ms | +| Playwright actionability | — | 309 ms | 303 ms | +| Server pickup | 0.8 ms | 2.2 ms | 0.8 ms | +| Scaffold | 41 ms | 54 ms | 43 ms | +| Write → first variant | 47 ms | 44 ms | 28 ms | +| First → all variants | 1.3 ms | 1.3 ms | 1.2 ms | -The plain path spends 90.4% of its model-free latency before the generate request leaves the browser. `handleGo()` always calls `captureAndEmit()`, which captures the element for the generating shader even when there are no annotations to upload. The annotation-path difference needs a capture microbenchmark before changing branch logic. +Dispatch-before-capture reduces the median model-free Pick → first-variant loop by **54.8%**, from 916 ms to 414 ms on the same fixture and deterministic agent. The refined in-browser probe shows that Live begins the plain generate fetch in **2.2 ms median**; most of the remaining 414 ms total is Playwright actionability, not product work. The product-side estimate after subtracting that automation delay is about **105 ms**. + +The annotated control still blocks on screenshot capture and upload by design. Its browser handler → fetch time is 46.7 ms median, and the E2E event retains comments, strokes, and `screenshotPath`. ## Harness evidence @@ -44,7 +50,7 @@ The plain path spends 90.4% of its model-free latency before the generate reques ### 1. Dispatch first, capture second -For unannotated picks, send `generate` immediately, then capture the shader texture concurrently. Preserve annotated capture as blocking until the protocol supports attaching screenshot evidence after event pickup. This targets the measured 828 ms plain-path bottleneck and does not touch generation quality. +**Shipped.** Unannotated picks send `generate` and wait only for the helper to accept it, then continue shader capture off the critical path. Annotated capture remains blocking because the screenshot is semantic model input. This produced the 54.8% median reduction above without changing the agent payload or provider path. ### 2. Progressive variant delivery @@ -95,6 +101,8 @@ Run a fast producer first and validate identity, copy preservation, param wiring Replace generic generating dots with truthful stage states from the journal: preparing capture, locating source, generating, previewing variant 1, and finishing alternatives. Never display fake percentage progress. -## Follow-up goal +## Completed goal Reduce the median plain Pick → first usable variant latency by at least 25% on the measured Vite/Chromium protocol baseline and materially reduce model-backed time-to-first-variant, while holding source validity, copy preservation, visual-quality acceptance, and Claude/Codex harness compatibility at or above baseline. Implement the unannotated dispatch-before-capture path first, then progressive delivery or a warm provider-specific producer only when their own benchmarks show a net win. + +Result: **54.8% median reduction** on the deterministic protocol benchmark. The next goal should target model-dominated sessions: progressively reveal the first valid variant, then benchmark a warm provider-specific producer only if it beats main-thread generation without reducing visual acceptance. diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json index 857546034..2604b8470 100644 --- a/plugin/.claude-plugin/plugin.json +++ b/plugin/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "impeccable", "description": "Design fluency for frontend development. 1 skill with 23 commands (/impeccable polish, /impeccable audit, /impeccable critique, etc.) and curated anti-pattern detection.", - "version": "4.0.0-alpha.8", + "version": "4.0.0-alpha.9", "author": { "name": "Paul Bakaus", "email": "paul@paulbakaus.com" diff --git a/plugin/skills/impeccable/SKILL.md b/plugin/skills/impeccable/SKILL.md index 9db246687..8645c49ff 100644 --- a/plugin/skills/impeccable/SKILL.md +++ b/plugin/skills/impeccable/SKILL.md @@ -1,7 +1,7 @@ --- name: impeccable description: Use when the user wants to design, redesign, shape, critique, audit, polish, clarify, distill, harden, optimize, adapt, animate, colorize, extract, or otherwise improve a frontend interface. Covers websites, landing pages, dashboards, product UI, app shells, components, forms, settings, onboarding, and empty states. Handles UX review, visual hierarchy, information architecture, cognitive load, accessibility, performance, responsive behavior, theming, anti-patterns, typography, fonts, spacing, layout, alignment, color, motion, micro-interactions, UX copy, error states, edge cases, i18n, and reusable design systems or tokens. Also use for bland designs that need to become bolder or more delightful, loud designs that should become quieter, live browser iteration on UI elements, or ambitious visual effects that should feel technically extraordinary. Not for backend-only or non-UI tasks. -version: 4.0.0-alpha.8 +version: 4.0.0-alpha.9 user-invocable: true argument-hint: "[craft|shape · audit|critique · animate|bolder|colorize|delight|layout|overdrive|quieter|typeset · adapt|clarify|distill · harden|onboard|optimize|polish · init|document|extract|live] [target]" license: Apache 2.0 @@ -19,27 +19,13 @@ Approach every design task as the design lead at a small studio known for giving 1. Run `node .claude/skills/impeccable/scripts/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's PRODUCT.md and DESIGN.md when they exist; follow what it prints, including any `UPDATE_AVAILABLE` directive (ask once, never block). If it reports `NO_PRODUCT_MD`: for `init`, `teach`, `craft`, `shape`, or wording that clearly maps to a from-scratch build flow, divert into `reference/init.md` first, **unless no user can respond** (a one-shot or automated run, or the user said not to ask): then write your own one-paragraph understanding of the product, audience, and the page's job from the brief and continue. For scoped evaluate/refine/fix requests against existing code, never divert into init; the existing code is the context. 2. If the user invoked a sub-command (`audit`, `polish`, `live`, ...), read **`reference/.md`** (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. On unattended runs, `craft` and `shape` collapse into this file's process: decide, record each decision in one line, build, self-review. 3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). -4. If the project is brand-new (no committed tokens, fonts, or brand colors found in step 3), run `node .claude/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed breaks your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this step entirely when step 3 found committed brand colors: identity-preservation wins. - ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this file warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. +**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. -**Existing worlds are sacred.** When the surface already has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside it: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Treat the task as new identity work only when nothing committed exists, or when the user asks for a redesign that discards the current look. A redesign is new work: derive the concept from the subject and the brief, not from the incumbent page's structure or styling. +**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. -**Ground it in the subject.** Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. - -**Plan, self-check, build.** Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. - -**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. - -**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. - -**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. - -**Commit.** Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. - -**Calibration.** AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. +**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Not optional, not skippable under time pressure: producing new identity without it yields the generic default this skill exists to prevent. A redesign is new work; derive the concept from the subject and the brief, not from the incumbent page's structure or styling. `context.mjs` prints this directive when it detects the situation. Scoped fixes inside an existing world don't need the playbook; the craft floor below governs them. ## Craft floor @@ -57,7 +43,7 @@ Build to this floor without announcing it. The design detector (the project hook Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. The brief and the surface decide the mode. PRODUCT.md's `register` field names the family (`brand` covers Persuade and Experience; `product` covers Operate and Read), and `reference/brand.md` / `reference/product.md` carry extended depth for the two families. -**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. +**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). **Operate** (app UI, dashboards, admin, tools; design SERVES the task). A person getting something done: density, scanability, and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. diff --git a/plugin/skills/impeccable/reference/new-work.md b/plugin/skills/impeccable/reference/new-work.md new file mode 100644 index 000000000..13c8f054b --- /dev/null +++ b/plugin/skills/impeccable/reference/new-work.md @@ -0,0 +1,37 @@ +# New identity work + +You are reading this because nothing committed exists yet (greenfield), or the user asked for a redesign that discards the current look. Either way the task is the same: invent a visual identity that could not be mistaken for anyone else's, and build it to the craft floor. SKILL.md's rules all still apply; this file is the process that produces the identity. + +## Seed + +If the project is brand-new (no committed tokens, fonts, or brand colors found in the code), run `node .claude/skills/impeccable/scripts/palette.mjs` for a brand seed color. The seed exists to break your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this entirely when the code already has committed brand colors: identity-preservation wins. + +## Ground it in the subject + +Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. + +## Plan, self-check, build + +Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. + +**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. + +**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. + +**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. + +## Commit + +Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. + +## Type and imagery for new Persuade and Experience surfaces + +Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. (For Operate and Read surfaces the opposite holds: system stacks and workhorse UI faces are legitimate and often correct.) + +Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Verify stock URLs resolve before shipping them. + +## Calibration + +AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. + +Before finishing, verify against SKILL.md's craft floor and run the detector; a bold page that ships mechanical defects is not done. diff --git a/plugin/skills/impeccable/scripts/context.mjs b/plugin/skills/impeccable/scripts/context.mjs index 11f2aabe0..15829a157 100644 --- a/plugin/skills/impeccable/scripts/context.mjs +++ b/plugin/skills/impeccable/scripts/context.mjs @@ -904,6 +904,10 @@ async function cli() { 'reference/init.md and write PRODUCT.md first; for any other (scoped) ' + 'command against existing code, proceed using the code as context and ' + `offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, + 'NEW_WORK: No committed design context was found. If this task produces ' + + 'new design (a build from scratch, or a redesign that discards the ' + + 'current look), you MUST read reference/new-work.md before making any ' + + 'design decision. Scoped fixes to existing code do not need it.', ]; parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { @@ -922,10 +926,22 @@ async function cli() { parts.push(buildMissingTargetDirective()); } const register = extractRegister(ctx.product); - const next = register - ? `NEXT STEP: This project's register is \`${register}\`. You MUST now read \`reference/${register}.md\` before producing any design output.` - : `NEXT STEP: You MUST now read the matching register reference (\`reference/brand.md\` or \`reference/product.md\`) before producing any design output. Pick based on PRODUCT.md above.`; - parts.push(next); + // The register field survives as a family hint (brand = Persuade/Experience, + // product = Operate/Read); SKILL.md's mode section carries the essentials + // inline, so no register-file read is mandated here. What IS mandated: + // the new-work playbook when no committed design system exists yet. + if (!ctx.hasDesign) { + parts.push( + 'NEW_WORK: PRODUCT.md exists but no DESIGN.md was found. If the code ' + + 'has no committed design system either (check the project files), and ' + + 'this task produces new design or a redesign that discards the current ' + + 'look, you MUST read reference/new-work.md before making any design ' + + 'decision. Scoped fixes inside an existing design system do not need it.', + ); + } + if (register) { + parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md; \`reference/${register}.md\` has extended depth for substantial work.`); + } const platform = extractPlatform(ctx.product); const nativeRefs = platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; @@ -933,7 +949,7 @@ async function cli() { const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to the register reference.`, + `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, ); } else if (!platform) { // A `## Platform` section that names something we don't recognize (a diff --git a/plugin/skills/impeccable/scripts/live-browser.js b/plugin/skills/impeccable/scripts/live-browser.js index 616f1290a..b0dcaa404 100644 --- a/plugin/skills/impeccable/scripts/live-browser.js +++ b/plugin/skills/impeccable/scripts/live-browser.js @@ -7105,6 +7105,14 @@ } async function captureAndEmit(el, basePayload, snapshot, rect) { + const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); + + // Plain requests do not send a screenshot to the agent, so capture is + // presentation-only. Wait only for the helper to accept the event before + // starting CPU-heavy capture; this yields the browser task and prevents + // rasterization from delaying the fetch itself. + if (!hasAnnotations) await sendEvent(basePayload); + let screenshotPath; let blob; let paper; @@ -7122,7 +7130,6 @@ // are present. Without annotations the image is pure visual anchoring - // it biases the model toward the current rendering and works against the // three-distinct-directions brief. - const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); if (blob && hasAnnotations) { try { const uploadRes = await fetch( @@ -7140,7 +7147,11 @@ console.warn('[impeccable] annotation upload failed:', err); } } - sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + // Annotated requests must wait for capture + upload because the screenshot + // is semantic input. Plain requests were already dispatched above. + if (hasAnnotations) { + sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + } } // diff --git a/site/data/live-harnesses.ts b/site/data/live-harnesses.ts index 495bdf5f8..ac4a437d2 100644 --- a/site/data/live-harnesses.ts +++ b/site/data/live-harnesses.ts @@ -39,11 +39,11 @@ export const harnessPaths = [ export const liveExperiments = [ { rank: 1, - title: 'Dispatch first, capture second', - evidence: 'The plain path spends 828 ms before the generate POST leaves the browser.', - move: 'For unannotated picks, post the event immediately and run shader capture in parallel. Keep annotated capture on the blocking path until the protocol can attach evidence later.', - expected: 'Removes up to 90% of the measured model-free floor on the common path.', - confidence: 'Measured', + title: 'Dispatch first, capture second — shipped', + evidence: 'Plain click-handler → generate fetch is now 2.2 ms median; end-to-end model-free latency fell from 916 ms to 414 ms.', + move: 'Unannotated picks wait for the helper to accept the event, then capture the shader off-path. Annotated picks still capture and upload before dispatch.', + expected: 'Delivered a 54.8% median reduction on the same fixture and deterministic agent.', + confidence: 'Measured + shipped', }, { rank: 2, @@ -111,4 +111,3 @@ export const currentHarnessProbe = { surfacedAutomatically: false, result: 'The sentinel appeared only after an explicit session read; no app terminal was attached.', }; - diff --git a/site/data/live-performance.json b/site/data/live-performance.json index c648f3fef..5792040a6 100644 --- a/site/data/live-performance.json +++ b/site/data/live-performance.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "generatedAt": "2026-07-11T23:46:48.581Z", + "generatedAt": "2026-07-12T00:14:29.714Z", "reports": [ { "schemaVersion": 1, @@ -373,6 +373,422 @@ } ], "harnessProbe": null + }, + { + "schemaVersion": 1, + "generatedAt": "2026-07-12T00:14:09.714Z", + "benchmark": { + "fixture": "vite8-react-plain", + "agent": "fake", + "provider": "deterministic", + "model": null, + "scenario": "plain", + "variants": 3, + "delivery": "atomic" + }, + "setup": { + "dependencies": 689.2, + "liveServer": 236.71, + "injection": 29.09, + "devServer": 541.5, + "pageLoad": 94.45, + "handshake": 263.64 + }, + "summary": { + "count": 7, + "metrics": { + "browserPreparationMs": { + "median": 311.53, + "p95": 825.97, + "min": 307.22, + "max": 827.73 + }, + "browserDispatchMs": { + "median": 2.2, + "p95": 5.26, + "min": 2, + "max": 6.4 + }, + "automationClickMs": { + "median": 309.33, + "p95": 820.89, + "min": 305.22, + "max": 821.33 + }, + "serverPickupMs": { + "median": 2.23, + "p95": 6.44, + "min": 0.96, + "max": 7.69 + }, + "goToAgentMs": { + "median": 313.51, + "p95": 832.1, + "min": 309.45, + "max": 835.42 + }, + "scaffoldMs": { + "median": 54.19, + "p95": 54.74, + "min": 51.07, + "max": 54.85 + }, + "generationMs": { + "median": 0.03, + "p95": 0.16, + "min": 0.03, + "max": 0.19 + }, + "writeMs": { + "median": 0.42, + "p95": 0.69, + "min": 0.38, + "max": 0.77 + }, + "writeToFirstVariantMs": { + "median": 44.39, + "p95": 76.33, + "min": 41.73, + "max": 86.24 + }, + "replyMs": { + "median": 2.01, + "p95": 2.97, + "min": 1.95, + "max": 3.26 + }, + "goToFirstVariantMs": { + "median": 414.2, + "p95": 960.68, + "min": 409.19, + "max": 973.99 + }, + "goToAllVariantsMs": { + "median": 416.61, + "p95": 962.65, + "min": 410.41, + "max": 976.22 + }, + "deliveryGapMs": { + "median": 1.26, + "p95": 2.36, + "min": 1.2, + "max": 2.41 + }, + "impeccableOverheadMs": { + "median": 414.17, + "p95": 960.53, + "min": 409.15, + "max": 973.8 + } + } + }, + "runs": [ + { + "iteration": 1, + "scenario": "plain", + "eventId": "9bbde352", + "browserPreparationMs": 827.73, + "browserDispatchMs": 6.4, + "automationClickMs": 821.33, + "serverPickupMs": 7.69, + "goToAgentMs": 835.42, + "scaffoldMs": 51.07, + "generationMs": 0.19, + "writeMs": 0.77, + "writeToFirstVariantMs": 86.24, + "replyMs": 3.26, + "goToFirstVariantMs": 973.99, + "goToAllVariantsMs": 976.22, + "deliveryGapMs": 2.24, + "impeccableOverheadMs": 973.8 + }, + { + "iteration": 2, + "scenario": "plain", + "eventId": "f21d7bba", + "browserPreparationMs": 821.86, + "browserDispatchMs": 2, + "automationClickMs": 819.86, + "serverPickupMs": 0.96, + "goToAgentMs": 822.82, + "scaffoldMs": 53.02, + "generationMs": 0.08, + "writeMs": 0.39, + "writeToFirstVariantMs": 53.22, + "replyMs": 2.15, + "goToFirstVariantMs": 929.64, + "goToAllVariantsMs": 930.99, + "deliveryGapMs": 1.35, + "impeccableOverheadMs": 929.56 + }, + { + "iteration": 3, + "scenario": "plain", + "eventId": "8641ce90", + "browserPreparationMs": 310.72, + "browserDispatchMs": 2.6, + "automationClickMs": 308.12, + "serverPickupMs": 2.37, + "goToAgentMs": 313.09, + "scaffoldMs": 53.46, + "generationMs": 0.03, + "writeMs": 0.42, + "writeToFirstVariantMs": 43.16, + "replyMs": 2.3, + "goToFirstVariantMs": 410.21, + "goToAllVariantsMs": 411.41, + "deliveryGapMs": 1.2, + "impeccableOverheadMs": 410.18 + }, + { + "iteration": 4, + "scenario": "plain", + "eventId": "bde64b84", + "browserPreparationMs": 311.14, + "browserDispatchMs": 2.3, + "automationClickMs": 308.84, + "serverPickupMs": 1.75, + "goToAgentMs": 312.88, + "scaffoldMs": 54.47, + "generationMs": 0.03, + "writeMs": 0.49, + "writeToFirstVariantMs": 43.77, + "replyMs": 2.01, + "goToFirstVariantMs": 411.69, + "goToAllVariantsMs": 412.95, + "deliveryGapMs": 1.26, + "impeccableOverheadMs": 411.66 + }, + { + "iteration": 5, + "scenario": "plain", + "eventId": "9198fca0", + "browserPreparationMs": 307.22, + "browserDispatchMs": 2, + "automationClickMs": 305.22, + "serverPickupMs": 2.23, + "goToAgentMs": 309.45, + "scaffoldMs": 54.85, + "generationMs": 0.04, + "writeMs": 0.42, + "writeToFirstVariantMs": 44.39, + "replyMs": 2.01, + "goToFirstVariantMs": 409.19, + "goToAllVariantsMs": 410.41, + "deliveryGapMs": 1.22, + "impeccableOverheadMs": 409.15 + }, + { + "iteration": 6, + "scenario": "plain", + "eventId": "c4273708", + "browserPreparationMs": 820.82, + "browserDispatchMs": 2.1, + "automationClickMs": 818.72, + "serverPickupMs": 3.53, + "goToAgentMs": 824.35, + "scaffoldMs": 54.43, + "generationMs": 0.03, + "writeMs": 0.41, + "writeToFirstVariantMs": 41.73, + "replyMs": 1.95, + "goToFirstVariantMs": 920.98, + "goToAllVariantsMs": 922.2, + "deliveryGapMs": 1.22, + "impeccableOverheadMs": 920.95 + }, + { + "iteration": 7, + "scenario": "plain", + "eventId": "ffed990a", + "browserPreparationMs": 311.53, + "browserDispatchMs": 2.2, + "automationClickMs": 309.33, + "serverPickupMs": 1.98, + "goToAgentMs": 313.51, + "scaffoldMs": 54.19, + "generationMs": 0.03, + "writeMs": 0.38, + "writeToFirstVariantMs": 46.06, + "replyMs": 2.01, + "goToFirstVariantMs": 414.2, + "goToAllVariantsMs": 416.61, + "deliveryGapMs": 2.41, + "impeccableOverheadMs": 414.17 + } + ], + "harnessProbe": null + }, + { + "schemaVersion": 1, + "generatedAt": "2026-07-12T00:14:29.712Z", + "benchmark": { + "fixture": "vite8-react-plain", + "agent": "fake", + "provider": "deterministic", + "model": null, + "scenario": "annotated", + "variants": 3, + "delivery": "atomic" + }, + "setup": { + "dependencies": 679.81, + "liveServer": 236.59, + "injection": 28.58, + "devServer": 518.43, + "pageLoad": 148.21, + "handshake": 263.27 + }, + "summary": { + "count": 3, + "metrics": { + "browserPreparationMs": { + "median": 349.77, + "p95": 402.2, + "min": 347.72, + "max": 408.03 + }, + "browserDispatchMs": { + "median": 46.7, + "p95": 99.35, + "min": 44.9, + "max": 105.2 + }, + "automationClickMs": { + "median": 302.83, + "p95": 303.05, + "min": 302.82, + "max": 303.07 + }, + "serverPickupMs": { + "median": 0.84, + "p95": 2.84, + "min": 0.81, + "max": 3.06 + }, + "goToAgentMs": { + "median": 350.61, + "p95": 405.04, + "min": 348.53, + "max": 411.09 + }, + "scaffoldMs": { + "median": 42.51, + "p95": 43.57, + "min": 37.9, + "max": 43.69 + }, + "generationMs": { + "median": 0.08, + "p95": 0.17, + "min": 0.03, + "max": 0.18 + }, + "writeMs": { + "median": 0.55, + "p95": 0.68, + "min": 0.38, + "max": 0.7 + }, + "writeToFirstVariantMs": { + "median": 31.94, + "p95": 91.06, + "min": 28.16, + "max": 97.63 + }, + "replyMs": { + "median": 2.15, + "p95": 2.99, + "min": 1.64, + "max": 3.08 + }, + "goToFirstVariantMs": { + "median": 423.69, + "p95": 535.33, + "min": 422.9, + "max": 547.73 + }, + "goToAllVariantsMs": { + "median": 424.9, + "p95": 537.87, + "min": 424.06, + "max": 550.42 + }, + "deliveryGapMs": { + "median": 1.21, + "p95": 2.54, + "min": 1.16, + "max": 2.69 + }, + "impeccableOverheadMs": { + "median": 423.61, + "p95": 535.16, + "min": 422.87, + "max": 547.55 + } + } + }, + "runs": [ + { + "iteration": 1, + "scenario": "annotated", + "eventId": "700d0eea", + "browserPreparationMs": 408.03, + "browserDispatchMs": 105.2, + "automationClickMs": 302.83, + "serverPickupMs": 3.06, + "goToAgentMs": 411.09, + "scaffoldMs": 37.9, + "generationMs": 0.18, + "writeMs": 0.7, + "writeToFirstVariantMs": 97.63, + "replyMs": 3.08, + "goToFirstVariantMs": 547.73, + "goToAllVariantsMs": 550.42, + "deliveryGapMs": 2.69, + "impeccableOverheadMs": 547.55 + }, + { + "iteration": 2, + "scenario": "annotated", + "eventId": "fc262897", + "browserPreparationMs": 347.72, + "browserDispatchMs": 44.9, + "automationClickMs": 302.82, + "serverPickupMs": 0.81, + "goToAgentMs": 348.53, + "scaffoldMs": 42.51, + "generationMs": 0.08, + "writeMs": 0.55, + "writeToFirstVariantMs": 31.94, + "replyMs": 1.64, + "goToFirstVariantMs": 423.69, + "goToAllVariantsMs": 424.9, + "deliveryGapMs": 1.21, + "impeccableOverheadMs": 423.61 + }, + { + "iteration": 3, + "scenario": "annotated", + "eventId": "6dda2190", + "browserPreparationMs": 349.77, + "browserDispatchMs": 46.7, + "automationClickMs": 303.07, + "serverPickupMs": 0.84, + "goToAgentMs": 350.61, + "scaffoldMs": 43.69, + "generationMs": 0.03, + "writeMs": 0.38, + "writeToFirstVariantMs": 28.16, + "replyMs": 2.15, + "goToFirstVariantMs": 422.9, + "goToAllVariantsMs": 424.06, + "deliveryGapMs": 1.16, + "impeccableOverheadMs": 422.87 + } + ], + "harnessProbe": null } ] } diff --git a/site/pages/live-lab/index.astro b/site/pages/live-lab/index.astro index a3a30e8ac..e4e121147 100644 --- a/site/pages/live-lab/index.astro +++ b/site/pages/live-lab/index.astro @@ -6,18 +6,24 @@ import '../../styles/sub-pages.css'; import '../../styles/live-performance.css'; const reports = 'reports' in benchmarkData ? benchmarkData.reports : [benchmarkData]; -const plain = reports.find(report => report.benchmark.scenario === 'plain' && report.benchmark.agent === 'fake') || reports[0]; -const annotated = reports.find(report => report.benchmark.scenario === 'annotated' && report.benchmark.agent === 'fake'); +const plainReports = reports.filter(report => report.benchmark.scenario === 'plain' && report.benchmark.agent === 'fake'); +const annotatedReports = reports.filter(report => report.benchmark.scenario === 'annotated' && report.benchmark.agent === 'fake'); +const baseline = plainReports[0] || reports[0]; +const plain = plainReports.at(-1) || baseline; +const annotated = annotatedReports.at(-1); const metric = (name: string) => plain.summary.metrics[name]?.median || 0; const protocolFloor = metric('goToFirstVariantMs'); -const browserPreparation = metric('browserPreparationMs'); +const baselineFloor = baseline.summary.metrics.goToFirstVariantMs?.median || protocolFloor; +const browserDispatch = metric('browserDispatchMs'); +const automationClick = metric('automationClickMs'); const serverPickup = metric('serverPickupMs'); const scaffold = metric('scaffoldMs'); const writeAndRender = metric('writeMs') + metric('writeToFirstVariantMs'); -const overlapFloor = Math.max(0, protocolFloor - browserPreparation); -const preparationShare = protocolFloor ? (browserPreparation / protocolFloor) * 100 : 0; +const productFloor = Math.max(0, protocolFloor - automationClick); +const improvement = baselineFloor ? Math.max(0, 1 - (protocolFloor / baselineFloor)) : 0; const annotatedFloor = annotated?.summary.metrics.goToFirstVariantMs?.median || 0; -const maxScenarioFloor = Math.max(protocolFloor, annotatedFloor, 1); +const maxScenarioFloor = Math.max(baselineFloor, protocolFloor, annotatedFloor, 1); +const optimizedTapeWidth = Math.max(12, (protocolFloor / baselineFloor) * 952); const generatedDate = new Date(plain.generatedAt).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', }); @@ -29,8 +35,8 @@ const displayMs = (value: number) => { }; const stageData = [ - { name: 'Browser preparation', value: browserPreparation, tone: 'capture' }, - { name: 'Server pickup', value: serverPickup, tone: 'pickup' }, + { name: 'Playwright actionability', value: automationClick, tone: 'capture' }, + { name: 'Live dispatch + pickup', value: browserDispatch + serverPickup, tone: 'pickup' }, { name: 'Source scaffold', value: scaffold, tone: 'scaffold' }, { name: 'Write + browser settle', value: writeAndRender, tone: 'render' }, ]; @@ -63,15 +69,15 @@ const stageData = [
-

Live latency lab · protocol baseline

-

The wait starts
before the model.

+

Live latency lab · optimization result

+

Capture left
the critical path.

- With generation removed, Go still takes {displayMs(protocolFloor)} to show the first variant. - {' '}{preparationShare.toFixed(1)}% of that time passes before the generate event leaves the browser. + The same model-free run fell from {displayMs(baselineFloor)} to {displayMs(protocolFloor)}, a{' '} + {(improvement * 100).toFixed(1)}% reduction. Live now starts the generate fetch in {displayMs(browserDispatch)} median.

{plain.summary.count} warm runs @@ -85,7 +91,7 @@ const stageData = [

Click → first usable variant

-

The measured critical path

+

The critical path moved

Median values. Deterministic generation isolates Impeccable’s own floor.

@@ -93,25 +99,15 @@ const stageData = [
Impeccable Live model-free latency timeline - A 916 millisecond timeline dominated by 828 milliseconds of browser preparation before the generate request. - - - - - - - Browser prepares capture - {displayMs(browserPreparation)} - everything after dispatch · {displayMs(overlapFloor)} - - Go - First variant + The same model-free benchmark improved from {displayMs(baselineFloor)} to {displayMs(protocolFloor)} median. + Before · capture blocks dispatch + + {displayMs(baselineFloor)} + After · dispatch precedes capture + + {displayMs(protocolFloor)} + + −{(improvement * 100).toFixed(1)}% · same fixture and deterministic agent
@@ -126,55 +122,60 @@ const stageData = [
- Finding 01 -

The browser captures the selected element for the generating shader even when no annotations need to reach the model. That capture blocks event dispatch.

+ Result 01 +

Unannotated requests wait only for the helper to accept the event. Shader capture continues off-path; annotated requests still capture and upload first because the screenshot is semantic input.

-

Capture paths

-

Annotation state changes the floor

+

Before / after / control

+

The common path is decisively faster

-

The difference is repeatable enough to investigate, not yet explained enough to optimize against.

+

The annotated path remains a control: its screenshot must reach the agent before generation starts.

-

Latency simulator

-

Model speed does not erase fixed overhead

+

The gain survives slower models

-

Move the model time. The protocol floor stays put.

+

Move model time to compare the old floor with the optimized product-side estimate.

- Current atomic path + Before optimization 15.9 s - model + measured protocol floor + model + measured baseline
- Capture fully overlapped + Current optimized path 15.1 s - best-case projection, not a measured target + model + measured floor, excluding Playwright actionability
diff --git a/site/styles/live-performance.css b/site/styles/live-performance.css index f1d4286a7..83a561052 100644 --- a/site/styles/live-performance.css +++ b/site/styles/live-performance.css @@ -319,6 +319,7 @@ .scenario-track .is-plain { background: var(--ks-kinpaku); } .scenario-track .is-annotated { background: var(--ks-patina); } +.scenario-track .is-control { background: var(--ks-graphite-2); } .scenario-row output { color: var(--ks-champagne); diff --git a/skill/SKILL.src.md b/skill/SKILL.src.md index c648e4734..f668c3e3d 100644 --- a/skill/SKILL.src.md +++ b/skill/SKILL.src.md @@ -18,27 +18,13 @@ Approach every design task as the design lead at a small studio known for giving 1. Run `node {{scripts_path}}/context.mjs` once per session (if the runtime shows this skill's loaded base directory, run `node /scripts/context.mjs`; keep cwd at the user's project). It prints the project's PRODUCT.md and DESIGN.md when they exist; follow what it prints, including any `UPDATE_AVAILABLE` directive (ask once, never block). If it reports `NO_PRODUCT_MD`: for `init`, `teach`, `craft`, `shape`, or wording that clearly maps to a from-scratch build flow, divert into `reference/init.md` first, **unless no user can respond** (a one-shot or automated run, or the user said not to ask): then write your own one-paragraph understanding of the product, audience, and the page's job from the brief and continue. For scoped evaluate/refine/fix requests against existing code, never divert into init; the existing code is the context. 2. If the user invoked a sub-command (`audit`, `polish`, `live`, ...), read **`reference/.md`** (the `.native` variant from the Commands table when the platform is `ios`/`android`/`adaptive`) and follow it. On unattended runs, `craft` and `shape` collapse into this file's process: decide, record each decision in one line, build, self-review. 3. Read at least one project file (CSS / tokens / theme / a representative component) to learn what world you're in. If PRODUCT.md's `## Platform` is `ios` or `android`, also read `reference/.md` (`adaptive` reads both). -4. If the project is brand-new (no committed tokens, fonts, or brand colors found in step 3), run `node {{scripts_path}}/palette.mjs` for a brand seed color. The seed breaks your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this step entirely when step 3 found committed brand colors: identity-preservation wins. - ## How to design -**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this file warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. +**The brief wins.** Where the brief pins down a direction (a named aesthetic, an era, a place, a material, a specific font or palette), follow it exactly, including when it asks for a look this skill warns is saturated. Redirecting a pinned direction toward your own taste is a failure, not a save. -**Existing worlds are sacred.** When the surface already has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside it: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Treat the task as new identity work only when nothing committed exists, or when the user asks for a redesign that discards the current look. A redesign is new work: derive the concept from the subject and the brief, not from the incumbent page's structure or styling. +**Existing worlds are sacred.** Most of impeccable's work happens inside a site or app that already exists. When the surface has a committed design system (real tokens, deliberately chosen faces, a palette the brand owns), work inside that world: extend it, sharpen it, leave it unmistakably the same brand, and never degrade a working page's performance. Inventing parallel colors, fonts, or styles on an existing surface is a defect, not creativity. -**Ground it in the subject.** Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. - -**Plan, self-check, build.** Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. - -**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. - -**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. - -**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. - -**Commit.** Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. - -**Calibration.** AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. +**New identity work reads the playbook first.** When nothing committed exists (greenfield, or a codebase with no real tokens or chosen faces), or the user asks for a redesign that discards the current look, you MUST read [reference/new-work.md](reference/new-work.md) before making any design decision. Not optional, not skippable under time pressure: producing new identity without it yields the generic default this skill exists to prevent. A redesign is new work; derive the concept from the subject and the brief, not from the incumbent page's structure or styling. `context.mjs` prints this directive when it detects the situation. Scoped fixes inside an existing world don't need the playbook; the craft floor below governs them. ## Craft floor @@ -68,7 +54,7 @@ Calibration for this provider: Name the visitor's mode before designing; the page's grammar follows from it, and most ruined pages are one mode wearing another mode's grammar. The brief and the surface decide the mode. PRODUCT.md's `register` field names the family (`brand` covers Persuade and Experience; `product` covers Operate and Read), and `reference/brand.md` / `reference/product.md` carry extended depth for the two families. -**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. +**Persuade** (landing pages, marketing, campaigns; design IS the product). The deliverable is an impression that stops the scroll, earns the click, converts. Spans every genre (tech, luxury, consumer); don't collapse them into one look. Briefs that imply imagery must ship real, verified imagery; a colored rectangle where a photo belongs reads as incomplete. New Persuade surfaces take their typeface procedure and reject list from [reference/new-work.md](reference/new-work.md). **Operate** (app UI, dashboards, admin, tools; design SERVES the task). A person getting something done: density, scanability, and consistency outrank expressiveness. These surfaces earn trust by feeling native to their platform: system font stacks and workhorse UI faces are legitimate and often correct here (the Persuade reject list does not apply). The brand lives in the details: focus states, empty states, microcopy, one owned accent. diff --git a/skill/reference/new-work.md b/skill/reference/new-work.md new file mode 100644 index 000000000..1e165fdb7 --- /dev/null +++ b/skill/reference/new-work.md @@ -0,0 +1,37 @@ +# New identity work + +You are reading this because nothing committed exists yet (greenfield), or the user asked for a redesign that discards the current look. Either way the task is the same: invent a visual identity that could not be mistaken for anyone else's, and build it to the craft floor. SKILL.md's rules all still apply; this file is the process that produces the identity. + +## Seed + +If the project is brand-new (no committed tokens, fonts, or brand colors found in the code), run `node {{scripts_path}}/palette.mjs` for a brand seed color. The seed exists to break your reflex palette; it does not override the subject. When the subject's world clearly dictates color (an era, a place, a material, a medium), derive the palette from that world and use the seed only to check yourself. Otherwise anchor on it. Use OKLCH throughout. Skip this entirely when the code already has committed brand colors: identity-preservation wins. + +## Ground it in the subject + +Name one concrete subject, its audience, and the page's single job. The subject's own world (its materials, instruments, artifacts, places, history, vernacular) is where distinctive choices come from. What would this thing look like as a physical object? What did its world look like before the web? A design whose subject appears only in the copy is a template wearing a costume. + +## Plan, self-check, build + +Plan a compact token system in your reasoning: palette, type, layout concept in one sentence, and a **signature**: the one element this page will be remembered by, drawn from the subject's world. A signature carries weight: sized and placed so the page organizes itself around it. The standard page skeleton is a default, not a given; derive structure from what the subject needs. Then audit the plan: work through what you'd produce for a similar brief from another client, and wherever the two plans converge (same palette family, same face, same skeleton), that part is your generic default, not a choice. Revise it, then build, deriving every color and type decision from the revised plan. + +**The opening viewport is a thesis, not a header.** Open with the most characteristic thing in the subject's world, in whatever form it takes: the product visibly working, an artifact from that world, the signature itself at full scale. A headline over two buttons is the template answer; earn it or replace it. The memory test: if a stranger scrolled past this page once, what would they describe an hour later? If the honest answer is a mood ("clean", "tasteful"), the concept hasn't committed yet. + +**Everything bold, nothing bland.** Bold is not decoration and not clutter; it is commitment to the concept, carried through every section. Commitment takes whatever form the concept demands: maximal or severely clean, drenched in color or nearly monochrome, copy so precise it stings, the product demonstrating itself instead of being described. A spare page built on one uncompromising idea is bold; a busy page of tasteful defaults is bland. The signature is where the concept peaks, not the only place it lives; cut anything that neither advances the concept nor serves the brief. Polish is the floor, not the point: when torn between refined and committed, commit. + +**Prove, don't claim.** A page earns belief by showing the product doing its job: the interface at work, the mechanism dramatized, numbers and specifics a competitor couldn't copy-paste. A reader should understand what it does by looking, before reading a word. Sections that restate the hero's claim in different words add length, not substance. + +## Commit + +Pick a color strategy before picking colors: Restrained (neutrals + one accent; the default when the visitor came to operate or read) / Committed (one saturated color carries 30-60% of the surface) / Full palette (3-4 named roles) / Drenched (the surface IS the color). Persuade and Experience surfaces have permission for the bolder strategies; take them when the brief allows. Dark vs. light is never a default: write one sentence of physical scene (who uses this, where, under what light, in what mood) and let it force the answer. The warm cream near-white body background is the saturated AI default; where the axis is free, pick a background that is a choice. + +## Type and imagery for new Persuade and Experience surfaces + +Choose faces like objects from the brand's world; these training-data defaults mean you stopped looking: Fraunces, Playfair Display, Cormorant, Lora, Crimson, Newsreader, Syne, Space Grotesk, Space Mono, IBM Plex, Inter-as-display, DM Sans, DM Serif, Outfit, Plus Jakarta Sans, Instrument Sans. (For Operate and Read surfaces the opposite holds: system stacks and workhorse UI faces are legitimate and often correct.) + +Imagery-implying briefs (food, travel, place, product, fashion) must ship real, verified imagery, searched for the brand's physical object rather than the category; a colored rectangle where a photo belongs reads as incomplete, and one decisive photo beats five mediocre ones. Verify stock URLs resolve before shipping them. + +## Calibration + +AI-generated interfaces cluster around a few looks regardless of subject: warm cream + high-contrast serif + terracotta accent; near-black + one neon accent (acid green, cyan) + glowing edges; broadsheet-editorial hairlines + italic display serif + small tracked mono labels. All are legitimate when the brief calls for them; the brief always wins. Where the brief leaves the aesthetic free, landing in one of them means your self-check failed. Same one tier deeper: if someone could guess your aesthetic from the category alone, or from category-plus-avoidance, rework until neither answer is obvious. + +Before finishing, verify against SKILL.md's craft floor and run the detector; a bold page that ships mechanical defects is not done. diff --git a/skill/scripts/context.mjs b/skill/scripts/context.mjs index 11f2aabe0..15829a157 100644 --- a/skill/scripts/context.mjs +++ b/skill/scripts/context.mjs @@ -904,6 +904,10 @@ async function cli() { 'reference/init.md and write PRODUCT.md first; for any other (scoped) ' + 'command against existing code, proceed using the code as context and ' + `offer \`${IMPECCABLE_COMMAND} init\` as a suggestion (do not block).`, + 'NEW_WORK: No committed design context was found. If this task produces ' + + 'new design (a build from scratch, or a redesign that discards the ' + + 'current look), you MUST read reference/new-work.md before making any ' + + 'design decision. Scoped fixes to existing code do not need it.', ]; parts.push(buildResolvedContextDirective(ctx, cliOptions, { targetExists })); if (shouldWarnMissingTarget(ctx, targetProvided, targetExists)) { @@ -922,10 +926,22 @@ async function cli() { parts.push(buildMissingTargetDirective()); } const register = extractRegister(ctx.product); - const next = register - ? `NEXT STEP: This project's register is \`${register}\`. You MUST now read \`reference/${register}.md\` before producing any design output.` - : `NEXT STEP: You MUST now read the matching register reference (\`reference/brand.md\` or \`reference/product.md\`) before producing any design output. Pick based on PRODUCT.md above.`; - parts.push(next); + // The register field survives as a family hint (brand = Persuade/Experience, + // product = Operate/Read); SKILL.md's mode section carries the essentials + // inline, so no register-file read is mandated here. What IS mandated: + // the new-work playbook when no committed design system exists yet. + if (!ctx.hasDesign) { + parts.push( + 'NEW_WORK: PRODUCT.md exists but no DESIGN.md was found. If the code ' + + 'has no committed design system either (check the project files), and ' + + 'this task produces new design or a redesign that discards the current ' + + 'look, you MUST read reference/new-work.md before making any design ' + + 'decision. Scoped fixes inside an existing design system do not need it.', + ); + } + if (register) { + parts.push(`REGISTER: \`${register}\` (family hint: brand = Persuade/Experience surfaces, product = Operate/Read). Derive the visitor's mode per SKILL.md; \`reference/${register}.md\` has extended depth for substantial work.`); + } const platform = extractPlatform(ctx.product); const nativeRefs = platform === 'adaptive' ? ['ios', 'android'] : platform === 'ios' || platform === 'android' ? [platform] : []; @@ -933,7 +949,7 @@ async function cli() { const refList = nativeRefs.map(p => `\`reference/${p}.md\``).join(' and '); const label = platform === 'adaptive' ? '`adaptive` (both iOS and Android)' : `\`${platform}\``; parts.push( - `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to the register reference.`, + `NEXT STEP: This project targets ${label}. Also read ${refList} for native conventions, in addition to SKILL.md's mode guidance.`, ); } else if (!platform) { // A `## Platform` section that names something we don't recognize (a diff --git a/skill/scripts/live-browser.js b/skill/scripts/live-browser.js index 616f1290a..b0dcaa404 100644 --- a/skill/scripts/live-browser.js +++ b/skill/scripts/live-browser.js @@ -7105,6 +7105,14 @@ } async function captureAndEmit(el, basePayload, snapshot, rect) { + const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); + + // Plain requests do not send a screenshot to the agent, so capture is + // presentation-only. Wait only for the helper to accept the event before + // starting CPU-heavy capture; this yields the browser task and prevents + // rasterization from delaying the fetch itself. + if (!hasAnnotations) await sendEvent(basePayload); + let screenshotPath; let blob; let paper; @@ -7122,7 +7130,6 @@ // are present. Without annotations the image is pure visual anchoring - // it biases the model toward the current rendering and works against the // three-distinct-directions brief. - const hasAnnotations = snapshot && (snapshot.comments.length > 0 || snapshot.strokes.length > 0); if (blob && hasAnnotations) { try { const uploadRes = await fetch( @@ -7140,7 +7147,11 @@ console.warn('[impeccable] annotation upload failed:', err); } } - sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + // Annotated requests must wait for capture + upload because the screenshot + // is semantic input. Plain requests were already dispatched above. + if (hasAnnotations) { + sendEvent(screenshotPath ? { ...basePayload, screenshotPath } : basePayload); + } } // diff --git a/tests/context.test.mjs b/tests/context.test.mjs index 540908d5e..a71844d2d 100644 --- a/tests/context.test.mjs +++ b/tests/context.test.mjs @@ -461,7 +461,7 @@ describe('loadContext (monorepo project context)', () => { assert.match(res.stdout, /"targetPath": "apps\/dashboard\/src\/App\.jsx"/); assert.match(res.stdout, /"productPath": "apps\/dashboard\/PRODUCT\.md"/); assert.match(res.stdout, /"designPath": "DESIGN\.md"/); - assert.match(res.stdout, /NEXT STEP: This project's register is `product`\./); + assert.match(res.stdout, /REGISTER: `product`/); }); it('asks for an app when the CLI runs from a monorepo root without selection', () => { @@ -806,8 +806,10 @@ describe('context.mjs CLI', () => { assert.match(res.stdout, /^# PRODUCT\.md/); assert.match(res.stdout, /# Acme/); assert.equal(res.stdout.includes('# DESIGN.md'), false); - // The NEXT STEP directive is always appended after `---`. - assert.match(res.stdout, /\n---\n\nNEXT STEP:/); + // Directives are appended after `---`; with no DESIGN.md the + // new-work gate directive fires. + assert.match(res.stdout, /\n---\n\n/); + assert.match(res.stdout, /NEW_WORK: PRODUCT\.md exists but no DESIGN\.md/); }); it('concatenates PRODUCT.md and DESIGN.md with a --- separator', async () => { @@ -819,7 +821,7 @@ describe('context.mjs CLI', () => { assert.match(res.stdout, /^# PRODUCT\.md/); assert.match(res.stdout, /\n---\n/); assert.match(res.stdout, /# DESIGN\.md\n\n# Acme design/); - assert.match(res.stdout, /NEXT STEP:/); + assert.equal(res.stdout.includes('NEW_WORK:'), false); }); it('reads from a fallback dir when cwd is clean', async () => { @@ -836,8 +838,8 @@ describe('context.mjs CLI', () => { const { spawnSync } = await import('node:child_process'); const res = spawnSync(process.execPath, [SCRIPT_PATH], { cwd: scratch, encoding: 'utf8', env: { ...process.env, IMPECCABLE_NO_UPDATE_CHECK: '1' } }); assert.equal(res.status, 0); - assert.match(res.stdout, /NEXT STEP: This project's register is `brand`\./); - assert.match(res.stdout, /read `reference\/brand\.md`/); + assert.match(res.stdout, /REGISTER: `brand`/); + assert.match(res.stdout, /reference\/brand\.md/); }); it('falls back to a generic register directive when no register field is present', async () => { @@ -845,8 +847,8 @@ describe('context.mjs CLI', () => { const { spawnSync } = await import('node:child_process'); const res = spawnSync(process.execPath, [SCRIPT_PATH], { cwd: scratch, encoding: 'utf8', env: { ...process.env, IMPECCABLE_NO_UPDATE_CHECK: '1' } }); assert.equal(res.status, 0); - assert.match(res.stdout, /NEXT STEP: You MUST now read the matching register reference/); - assert.match(res.stdout, /reference\/brand\.md.*reference\/product\.md/); + assert.match(res.stdout, /NEW_WORK: PRODUCT\.md exists but no DESIGN\.md/); + assert.equal(res.stdout.includes('REGISTER:'), false); }); it('appends a native platform directive for an ios project', async () => { diff --git a/tests/live-browser-source.test.mjs b/tests/live-browser-source.test.mjs index dc62d88bc..d03457908 100644 --- a/tests/live-browser-source.test.mjs +++ b/tests/live-browser-source.test.mjs @@ -5,8 +5,26 @@ import { join } from 'node:path'; const SOURCE = readFileSync(join(process.cwd(), 'skill/scripts/live-browser.js'), 'utf-8'); const PENDING_DOCK_POSITION_SOURCE = SOURCE.match(/function positionPendingDock\(\) \{[\s\S]*?\n \}/)?.[0] || ''; +const CAPTURE_AND_EMIT_SOURCE = SOURCE.match(/async function captureAndEmit\([\s\S]*?\n \}/)?.[0] || ''; describe('live-browser source contracts', () => { + it('dispatches plain generation before screenshot capture without bypassing annotated evidence', () => { + const dispatchIndex = CAPTURE_AND_EMIT_SOURCE.indexOf('if (!hasAnnotations) await sendEvent(basePayload);'); + const captureIndex = CAPTURE_AND_EMIT_SOURCE.indexOf('await captureElementToBlob'); + assert.ok(dispatchIndex >= 0, 'plain generation should dispatch immediately'); + assert.ok(captureIndex > dispatchIndex, 'plain generation dispatch must happen before capture begins'); + assert.match( + CAPTURE_AND_EMIT_SOURCE, + /if \(blob && hasAnnotations\)[\s\S]*?\/annotation\?token=/, + 'annotation screenshots should still upload before annotated generation dispatch', + ); + assert.match( + CAPTURE_AND_EMIT_SOURCE, + /if \(hasAnnotations\) \{\s*sendEvent\(screenshotPath \? \{ \.\.\.basePayload, screenshotPath \} : basePayload\);\s*\}/, + 'annotated generation should dispatch exactly after capture and upload resolve', + ); + }); + it('saves copy edits to the staged buffer with rich AI context', () => { assert.doesNotMatch( SOURCE,