mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-20 01:56:37 +03:00
update
This commit is contained in:
@@ -73,7 +73,7 @@ If a `DESIGN.md` already exists, **do not silently overwrite it**. Show the user
|
||||
## Two paths
|
||||
|
||||
- **Scan mode** (default): the project has design tokens, components, or rendered output. Extract, then confirm descriptive language. Use when there's code to analyze.
|
||||
- **Seed mode**: the project is pre-implementation (fresh init, nothing built yet). Gather any existing brand assets, interview for five high-level answers, optionally generate visual cues for the palette pick, write a minimal DESIGN.md marked `<!-- SEED -->`. Re-run in scan mode once there's code.
|
||||
- **Seed mode**: the project is pre-implementation (fresh init, nothing built yet). Gather any existing brand assets, interview for five high-level answers, optionally generate visual cues and run the browser questionnaire, then write a seed DESIGN.md marked `<!-- SEED -->` that carries every decision the interview and the questionnaire made. Re-run in scan mode once there's code.
|
||||
|
||||
Decide by scanning first (Scan mode Step 1). If the scan finds no tokens, no component files, and no rendered site, offer seed mode; don't silently switch. `/impeccable document --seed` forces seed mode on a pre-implementation project, but it does not authorize replacing coherent code: when an incumbent system exists, offer scan mode or route an explicit identity-replacement request through new-work.
|
||||
|
||||
@@ -410,29 +410,50 @@ Interview answers are words; a palette is easier picked by eye. Before writing t
|
||||
- **No usable native path, no key**: pause and {{ask_instruction}} whether the user wants generated visual cues to pick a palette by eye. *"I can generate a few small palette-and-mood images so you choose a direction visually instead of from descriptions. That needs an image-generation API key (FLUX and Google Nano Banana are supported out of the box; other providers work too), stored as `IMAGE_GEN_API_KEY` in `.impeccable/.env`. Add one, or skip straight to the seed?"* If a key arrives, write it to `.impeccable/.env` together with `IMAGE_GEN_PROVIDER` (`bfl` for FLUX, `gemini` for Nano Banana, the provider's own name for anything else; when the user does not say, let the wrapper infer it from the key). Confirm that file is listed in the project's `.gitignore` (add it if missing; a committed key is a leak), then load [image-api.md](image-api.md). Its shipped wrapper is the whole integration for the built-in providers; only a provider it does not know earns the project-local wrapper that file specifies.
|
||||
- **The user opts out, or no key arrives**: go to Step 5 and seed from the answers alone.
|
||||
|
||||
When generation is available, **stop and load [visual-cues.md](visual-cues.md)** and follow its pipeline; it owns everything from the one-line user announcement and the persona palette studio through generation, `cues.json`, and the picker pause. Do not restate its mechanics here or in chat. Do not write DESIGN.md in that turn; Steps 5-6 run when a completed picker is handed to the later seed consumer, or immediately when the user opted out of generation.
|
||||
When generation is available, **stop and load [visual-cues.md](visual-cues.md)** and follow its pipeline; it owns everything from the one-line user announcement and the persona palette studio through generation, `cues.json`, and the picker pause. Do not restate its mechanics here or in chat. The picker's exit is the handoff: when the server exits 0 and `.impeccable/design-interview/answers.json` lands, come back here and run Steps 5-6 with that file in hand. When the user opted out of generation (or no key arrived), run Steps 5-6 immediately from the interview alone.
|
||||
|
||||
### Step 5: Write seed DESIGN.md
|
||||
|
||||
Use the canonical section order from Scan mode. Populate what the interview, the assets, and any cue pick answer; leave the rest as honest placeholders. The seed is a scaffold, not a fabricated spec.
|
||||
Use the canonical section order from Scan mode. Populate what the interview, the assets, and the questionnaire answer; leave the rest as honest placeholders. The seed is a scaffold, not a fabricated spec, but a decision the user actually made in the picker is real and belongs in the file at full strength.
|
||||
|
||||
Lead the file with:
|
||||
Mark the file as a seed with this comment as the first line of the markdown body, immediately after the frontmatter's closing `---` (the frontmatter must open the file or token parsers will not see it):
|
||||
|
||||
```markdown
|
||||
<!-- SEED: established with the user before implementation; re-run /impeccable document once there's code to capture the actual tokens and components. -->
|
||||
```
|
||||
|
||||
Per-section guidance in seed mode:
|
||||
**Two seeds exist**, and which one you write depends on whether Step 4's picker ran:
|
||||
|
||||
**Interview-only seed** (the user opted out of generation, or no key arrived). Per-section guidance:
|
||||
|
||||
- **Overview**: Creative North Star and philosophy phrased from the answers (color strategy + motion energy + references). Reference the user's anti-reference directly.
|
||||
- **Colors**: Color strategy as a Named Rule (e.g. *"The Drenched Rule. The surface IS the color."*). Hue family or anchor reference. Colors sampled from a provided logo, or from a cue image the user picked in Step 4, are real; include them with exact values and note the source. Everything else stays `[to be resolved during implementation]`; those sampled anchors are the only hex a seed may carry.
|
||||
- **Colors**: Color strategy as a Named Rule (e.g. *"The Drenched Rule. The surface IS the color."*). Hue family or anchor reference. Colors sampled from a provided logo are real; include them with exact values and note the source. Everything else stays `[to be resolved during implementation]`; those sampled anchors are the only hex this seed may carry.
|
||||
- **Typography**: the direction the user picked (e.g. "Serif display + sans body"). No font names yet: `[font pairing to be chosen at implementation]`.
|
||||
- **Layout** and **Shapes**: omit unless an asset or answer established a spatial or form preference; do not invent grids or corner language pre-implementation.
|
||||
- **Elevation & Depth**: inferred from motion energy. Restrained/Responsive → flat by default; Choreographed → layered. One sentence.
|
||||
- **Components**: omit entirely; no components exist yet.
|
||||
- **Do's and Don'ts**: carry PRODUCT.md's anti-references directly plus the anti-reference named in Q5.
|
||||
|
||||
Seed mode writes a minimal frontmatter with `name` and `description` only; no colors, typography, rounded, spacing, or components yet. Real tokens land on the next Scan-mode run. Skip the `.impeccable/design.json` sidecar in seed mode for the same reason: nothing to render.
|
||||
This seed writes a minimal frontmatter with `name` and `description` only; no colors, typography, rounded, spacing, or components yet.
|
||||
|
||||
**Questionnaire seed** (`.impeccable/design-interview/answers.json` exists from this run). The user answered every screen by eye, so the seed carries their answers as decisions, not directions. Read the answers file plus the picked cue's palette entry in `.impeccable/visual-cues/cues.json` (`palette-source` names it), and map:
|
||||
|
||||
- **Frontmatter**: `name` and `description`, plus real `colors` (the four `palette-*` hex values under descriptive slugs; these are picked, not sampled) and real `typography` (`font-heading` and `font-body` are exact family names; give each role its family and weight intent, leave sizes for implementation). Still no `rounded`, `spacing`, or `components`: the corner and spacing answers are qualitative, and nothing is built.
|
||||
- **Overview**: as the interview-only seed, plus name the chosen surfaces (`surface-modes`) and what each is for. Movement stays here, after the North Star, but the questionnaire asks it of a landing page and a portfolio only, so write what the keys support:
|
||||
- `motion-energy-<mode>` keys present, all agreeing: one philosophy sentence for the product, as before.
|
||||
- Keys present and disagreeing: one sentence per surface, named (*"The landing page moves on state change only; the portfolio stages entrances and drives sequences on scroll."*). The bare `motion-energy` is the leading one of the two.
|
||||
- **No `motion-energy` key at all**: the run has neither of those surfaces, so movement was never asked. Say nothing about it, and do not fill the gap from the interview's Q3 or from the register. The next Scan-mode run reads the real transitions out of the code.
|
||||
- **Colors**: the four roles with their picked hex, noting the cue they came from. `color-strategy` becomes the Named Rule. When surfaces differ (`color-strategy-<mode>` keys), state each surface's strategy and which surface leads (the bare key's owner).
|
||||
- **Typography**: the real pair by name, the pairing's character, and the type scale as a rule: `type-scale` names it, `type-scale-ratio` is the ratio (e.g. *"Major third: each heading step is 1.25x the last"*). Base size and exact steps stay `[resolved at implementation]`. A `font-heading-source` / `font-body-source` value means a user-provided font file; record where it lives.
|
||||
- **Layout**: `layout-structure` (how pages are composed) and `boundary-style` (how sections separate), per surface when the `-<mode>` keys differ. No invented grids beyond what the answer states.
|
||||
- **Elevation & Depth**: `depth-style` per surface, stated directly; the questionnaire answered this, so do not re-infer it from motion energy.
|
||||
- **Shapes**: `corner-style` per surface.
|
||||
- **Components**: still omit; nothing exists yet.
|
||||
- **Do's and Don'ts**: the interview-only guidance, plus a Do fixing the icon source: every icon comes from the chosen pack (`icon-pack-name`, license, URL), no mixed sets.
|
||||
|
||||
Per-surface answers come back for every chosen surface, defaults included, and a difference between surfaces is a decision the picker enforced, not an inconsistency to smooth over (the option lists differ per surface). Where all surfaces agree, state the answer once for the product. `motion-energy` is the one key that can be missing entirely, since only a landing page and a portfolio are asked about movement; [visual-cues.md](visual-cues.md) has the full contract.
|
||||
|
||||
Both seeds skip the `.impeccable/design.json` sidecar: nothing to render yet. Real tokens for sizes, spacing, and components land on the next Scan-mode run.
|
||||
|
||||
### Step 6: Confirm
|
||||
|
||||
@@ -441,6 +462,8 @@ Seed mode writes a minimal frontmatter with `name` and `description` only; no co
|
||||
|
||||
Your own write is the freshest source; no reload needed.
|
||||
|
||||
When the questionnaire ran, the confirm is not the end of the turn: the design context document in the user's tab is live for edits through the session the picker forked. Follow the document edit loop in [visual-cues.md](visual-cues.md): poll, apply `edit_request`s to this same DESIGN.md, reply. A color the user changed in the tab before your seed write is already in `answers.json`; one changed after lands in DESIGN.md without you (the session swaps the hex itself, journaled in `.impeccable/design-interview/doc-edits.jsonl` for the name-reconciliation pass at exit).
|
||||
|
||||
## Style guidelines
|
||||
|
||||
- **Frontmatter first, prose second.** Tokens go in the YAML frontmatter; prose contextualizes them. Don't redefine a token value in two places; the frontmatter is normative.
|
||||
|
||||
@@ -453,11 +453,31 @@ Done when: `fonts.json` is parseable, contains exactly six ranked pairs, every f
|
||||
|
||||
Before launching, write the surface set from Step 6 into `cues.json` as a top-level `modes` array: any of `persuade`, `operate`, `read`, `experience`. Do not re-derive it; the font pairs were composed against that reading, and a second judgment here would hand the user tiles the shortlist never answered to. The picker's first question pre-checks those tiles as its starting point; the user corrects the set by hand, and the final selection returns in the answers as `surface-modes`. Omit the field when the product gave no clear signal; the picker then starts from `persuade` alone.
|
||||
|
||||
Four of the questions are then answered per surface rather than once for the whole run, because the answer that suits a marketing page rarely suits the tool it sells: `color-strategy`, `boundary-style` (how sections are separated), `corner-style` (how round shapes are), and `depth-style` (how far off the page things sit). Each of the four comes back twice over. The bare key holds the leading surface's answer, which is the first chosen tile in tile order and the one every later screen previews. Alongside it is one `<key>-<mode>` key for every surface chosen, `<mode>` being `persuade`, `operate`, `read`, or `experience`. Surfaces the user never opened are included too, holding the default for their kind; a surface nobody chose returns nothing at all.
|
||||
In the same write, add a top-level `context` object carrying the chat half of the run, because after the last question the picker shows the user a design context document assembled from everything the interview learned, and the browser only knows what it asked itself. Every field is optional and the document renders whatever arrives, so fill what the run actually established and leave out the rest:
|
||||
|
||||
The picker does not offer every option on every surface. A landing page can take any answer to all four questions, and the other three surfaces have options withheld from them: a page people work in or read at length is not offered the loudest color or the deepest shadow, a tool is not offered separation by spacing alone, and a portfolio is not offered four working colors or fully round controls. So a value that comes back is one that suits the surface it came from, and a difference between two surfaces is a decision rather than an inconsistency to reconcile.
|
||||
```json
|
||||
"context": {
|
||||
"product": { "name": "[product name]", "purpose": "[one-sentence purpose from PRODUCT.md]" },
|
||||
"audience": { "primary": "[who]", "secondary": "[who]", "emotion": "[emotional goal on landing]", "needs": ["[need]"] },
|
||||
"brand": { "words": ["[word]"], "personality": "[one sentence from PRODUCT.md Brand Personality]" },
|
||||
"assets": ["[asset name: what Step 2 read off it]"],
|
||||
"interview": {
|
||||
"colorStrategy": "[Q1 pick]", "hueAnchor": "[Q1 anchor]",
|
||||
"typeDirection": "[Q2 pick]", "motionEnergy": "[Q3 pick]",
|
||||
"references": ["[Q4, all three]"], "antiReference": "[Q5]"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When more than one surface comes back, DESIGN.md says what each of them does with color, section separation, corner radius, and depth, instead of stating one answer for the product.
|
||||
Quote the user's answers, not paraphrases of them; the document labels interview fields as the questions they answered. A missing block renders as a pointer to where that truth lives (PRODUCT.md), so an old `cues.json` without `context` still produces a complete document.
|
||||
|
||||
Five of the questions are then answered per surface rather than once for the whole run, because the answer that suits a marketing page rarely suits the tool it sells: `color-strategy`, `motion-energy` (how much movement there is), `boundary-style` (how sections are separated), `corner-style` (how round shapes are), and `depth-style` (how far off the page things sit). Each of the five comes back twice over. The bare key holds the leading surface's answer, which is the first chosen tile in tile order and the one every later screen previews. Alongside it is one `<key>-<mode>` key for every surface chosen, `<mode>` being `persuade`, `operate`, `read`, or `experience`. Surfaces the user never opened are included too, holding the default for their kind; a surface nobody chose returns nothing at all.
|
||||
|
||||
`motion-energy` is the one exception to that shape, because the question is only put to two of the four surfaces. A landing page and a portfolio are watched, so how much they move is a house decision; a tool and a document are worked in, and their movement follows the interface. So the motion keys cover the chosen surfaces among `persuade` and `experience` only, and the bare key holds the first of those two in tile order rather than the run's leading surface: on an app UI plus portfolio run, `motion-energy` is the portfolio's answer. **When a run chooses neither of those surfaces the question is never asked, and no `motion-energy` key comes back at all.** Read it as absent rather than defaulted, and say nothing about movement in DESIGN.md; a default written as a decision is a decision the user never made.
|
||||
|
||||
The picker does not offer every option on every surface. A landing page can take any answer to all five questions, and the other three surfaces have options withheld from them: a page people work in or read at length is not offered the loudest color or the deepest shadow, a tool is not offered separation by spacing alone, and a portfolio is not offered four working colors or fully round controls. So a value that comes back is one that suits the surface it came from, and a difference between two surfaces is a decision rather than an inconsistency to reconcile.
|
||||
|
||||
When more than one surface comes back, DESIGN.md says what each of them does with color, movement, section separation, corner radius, and depth, instead of stating one answer for the product.
|
||||
|
||||
Tell the user in one line that the visual cues are ready at `.impeccable/visual-cues/` (name the count), then run `node {{scripts_path}}/picker-server.mjs` from the project root as a foreground command and parse its `PICKER_URL` line.
|
||||
|
||||
@@ -466,5 +486,32 @@ Tell the user in one line that the visual cues are ready at `.impeccable/visual-
|
||||
|
||||
The server process exiting is the completion signal; never poll or watch the answers file while it runs.
|
||||
|
||||
- **Exit 0**: read the `ANSWERS` path, tell the user the answers were received in one line, then stop. Do not show or describe the cues, ask for a pick in chat, or write DESIGN.md in this turn.
|
||||
- **Exit 0**: read the `ANSWERS` path, tell the user the answers were received in one line, then return to [document.md](document.md) Steps 5-6 and write the seed DESIGN.md from that file (its questionnaire-seed mapping owns which key lands where). Do not show or describe the cues or ask for a pick in chat; the picker already settled the pick. The user's tab is meanwhile showing the design context document the picker built from the run, and that document is now a working surface: on submit the server forked a detached edit session (`picker-doc-session.mjs`) that keeps the tab connected. After the seed DESIGN.md is written, enter the edit loop below.
|
||||
- **Exit 2**: tell the user the picker closed unanswered and that they can relaunch it with the same command. Never restart it unprompted.
|
||||
|
||||
## The document edit loop
|
||||
|
||||
The revealed document is editable in place, on live mode's division of labor:
|
||||
|
||||
- **Simple edits never reach you.** A palette color change is applied by the session process itself: it rewrites `answers.json`, swaps the old hex for the new one across DESIGN.md, and journals the change to `.impeccable/design-interview/doc-edits.jsonl`. If the color edit landed before your seed write, the answers file you seed from already carries it.
|
||||
- **Complex edits queue for you.** Font changes (including uploaded faces, saved under `.impeccable/design-interview/fonts/`) and freeform asks arrive as `edit_request` events.
|
||||
|
||||
After writing the seed DESIGN.md, tell the user in one line that the document in their tab is live for edits, then poll:
|
||||
|
||||
```
|
||||
node {{scripts_path}}/picker-doc-poll.mjs
|
||||
```
|
||||
|
||||
One-shot, exactly like live mode's poll: it blocks until one event and prints it as JSON. Run it on live mode's harness policy: on Claude Code as a background task; on Cursor as a one-shot poll in a background terminal with notify on `"type":"(edit_request|exit)"`; on Codex as a yielded foreground exec; elsewhere one-shot foreground. Never `--timeout` it short.
|
||||
|
||||
- `{"type":"edit_request", "id", "kind", "prompt", "category", "payload"}`: do the work. Apply the change to DESIGN.md (and `answers.json` where a questionnaire key names the same fact, so the tab re-renders it), move any uploaded font files where the project keeps assets, then reply and poll again:
|
||||
|
||||
```
|
||||
node {{scripts_path}}/picker-doc-poll.mjs --reply <id> done "One line the user sees in the tab"
|
||||
```
|
||||
|
||||
Reply `error` with a reason when the ask cannot be applied; reply `retry` to put it back in the queue untouched.
|
||||
- `{"type":"timeout"}`: nothing arrived in the budget; poll again.
|
||||
- `{"type":"exit"}`: the session ended (tab closed or timed out). Before moving on, read `doc-edits.jsonl` and reconcile any prose the deterministic edits left stale: a swapped hex whose descriptive color name in DESIGN.md no longer matches its value gets a fresh name. Then stop polling; the loop is over.
|
||||
|
||||
The user may keep working in chat while the document sits open; treat an `edit_request` like any other user instruction, just delivered through the tab.
|
||||
|
||||
Reference in New Issue
Block a user