From 62e90a257fae769557003ac621a346d7b17d6690 Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Tue, 11 Aug 2026 15:29:51 -0400 Subject: [PATCH 1/6] Comps are shipped screens: subject present, mode readable, depth over coverage Factory review evidence (two batches, both lanes): generated comps drift poster-ward. They render the world's atmosphere at high density, drop the surface's subject (a motorcycle forum comped with no motorcycles), and stop reading as screens a product would ship. The existing anti-vignette self-check catches the fully collapsed case but says nothing about density or subject presence, and new-work's "committed all the way" reads as a coverage instruction. Three sibling self-checks in visualize.md's comp discipline, each phrased per mode (Persuade/Operate/Read/Experience) and platform-neutral: the subject appears as the content the regions hold; the mode must be readable from the image alone; commitment is depth, not coverage, with one dominant move per viewport. new-work.md's decision-comp rule gains a clause binding the same checks so the direction round inherits them explicitly. AI-assisted (Claude Fable 5), prepared for maintainer review. Co-Authored-By: Claude Fable 5 --- skill/reference/new-work.md | 2 +- skill/reference/visualize.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/skill/reference/new-work.md b/skill/reference/new-work.md index f9b785dcd..343e2634f 100644 --- a/skill/reference/new-work.md +++ b/skill/reference/new-work.md @@ -48,7 +48,7 @@ The script assigns which structure gets built; your top-ranked structure is what The standing exit: every direction round offers one quiet, permanent alternative, the category standard, played straight. It is the user's door, never yours: never recommend it, never weigh it against the roll, never let it soften the dealt directions; the counterweights bind the unchosen default, not the chosen one. When the user takes it, in the canon action, a safer-steer, or plain words asking for the familiar or competitor-like path, convention becomes the commitment: ask once for two or three products this should sit alongside, make their craft level the bar, and execute the canon at full fidelity, without irony or smuggled quirk. A standing preference gets recorded as a brand commitment in PRODUCT.md. Re-roll eliminates every direction already shown, grounded and challenger alike; after two consecutive re-rolls, ask what quality is missing. You may re-roll on your own only on named factual grounds, when the assigned direction cannot carry the product's truth or task; taste is never grounds. The user may re-roll freely, and a user- or brief-pinned direction beats the roll, always. Present the decision visually: write an options payload with the assigned direction leading and its raised lines included, the pick card when one exists, the dealt challengers as alternates carrying their QUALITY BAR cards plus each challenger's verdict and kept line, re-roll with its safer and bolder registers, steer, plus canon enabled, and `followup: true` when the execution-contract round will follow (it does whenever image generation exists and no standing build-path preference is recorded); a degraded roll with no challengers still uses the page, as a single text-only card with re-roll. Give every card the same anatomy, thesis, palette, materials, first viewport, honest risk, and the challengers' case lines (run the script with `--schema` for the exact shape); the page renders identity from these fields, routes declined challengers to a demoted row on its own, and a challenger's catalog image rides as labeled inspiration, never as the promise of the build. Author `canonCard` too: the category standard as one honest card with the same anatomy; the page keeps it subordinate, and the counterweights still bind you. Run `node {{scripts_path}}/serve-question.mjs --start --payload ` (run it with `--schema` first for the exact payload shape). It daemonizes, prints the page URL and a key, and exits immediately; now open that URL for the user, in-app browser first, then the system opener, then showing the URL. Collect the choice with `--wait --key `, repeating while it exits 3; the ANSWER prints as JSON. Exit 4 means the page was closed without an answer: re-present once through the structured question tool, and with no answer there either, proceed unattended with the assigned direction and state the assumptions. A harness that can leave a shell blocked in the background may instead run the script without `--start` and let it auto-open and block. Only a session where no browser can open at all, headless, CI, an eval worker, a remote shell with no display, puts the same decision through the structured question tool instead; the script self-detects these environments and exits 2 with that advice, so treat exit 2 as this fallback, never as an error to retry. -When image generation exists, every card also declares a `sketch` path under `.impeccable/mocks/decision/` (the field keeps its wire name for compatibility; what it carries is the card's comp), the canon card included. Where the harness sandboxes its shell, start the page through the least-sandboxed command path it offers: a sandboxed shell cannot bind the board's port, and the first-attempt failure costs a retry every session. Serve the page first, then produce the comps; the page shimmer-waits per slot and the user may answer before they land. Each card's image is that direction's north-star comp at full fidelity, produced under the comp discipline in [visualize.md](visualize.md): the requested surface's first viewport, structure-led prompt, real product name and real content, no invented commercial claims, in that card's own palette, type character, and material world, committed all the way. Generation takes the same time at any fidelity, so an unfinished sketch pays sketch quality for comp cost; fairness between cards comes from equal fidelity in each card's own grammar, one surface, one aspect, never from shared unfinishedness. The frame's aspect is the surface's own: a native app or mobile-first surface comps portrait at its device viewport, a desktop web surface landscape, and the decision page adapts to either, so a phone screen comped landscape is a broken frame, not a neutral default. Produce in the order the user reads, the assigned card, then the pick, then the full-card hand, then canon, each file written with its prompt sidecar the moment it is done, so a re-roll's spend front-loads onto the cards read first; declined challengers get no comp, their catalog thumb is their face. When the harness runs subagents in parallel, fan the set out as one agent per card: each spawn is the shipped asset producer with a single-comp packet, that card's fields, PRODUCT.md, the shared frame, and the card's declared path, up to four in flight at once. A slot still empty when its agent returns is regenerated inline, and a slot still empty when the user answers is dropped without ceremony; no other supervision is owed. Without parallel subagents, generate in the main thread after serving, in the same reading order, and let the harness's own generation display carry the progress; the wait for the answer follows the last file. The chosen card's comp is not spent by the choice: on a comp-led build it enters the comp round as compositional option one, and on a code-led build it returns at the finish review as the critique reference, what the image dared that the build did not. The unchosen comps stay in `.impeccable/mocks/decision/` as the round's spent hand; they carry no approval and imply none. With no image generation, the cards carry their identity in palette chips and facts, and that page is complete, not a lesser version; the page then also demotes every challenger's catalog art to a labeled thumbnail on its own, because salience must encode the verdict, never the accident of which cards have images. +When image generation exists, every card also declares a `sketch` path under `.impeccable/mocks/decision/` (the field keeps its wire name for compatibility; what it carries is the card's comp), the canon card included. Where the harness sandboxes its shell, start the page through the least-sandboxed command path it offers: a sandboxed shell cannot bind the board's port, and the first-attempt failure costs a retry every session. Serve the page first, then produce the comps; the page shimmer-waits per slot and the user may answer before they land. Each card's image is that direction's north-star comp at full fidelity, produced under the comp discipline in [visualize.md](visualize.md): the requested surface's first viewport, structure-led prompt, real product name and real content, no invented commercial claims, in that card's own palette, type character, and material world, committed all the way, where commitment is depth, not coverage: visualize.md's self-checks, the mode readable from the image, one dominant move, the subject present as content, bind decision comps identically. Generation takes the same time at any fidelity, so an unfinished sketch pays sketch quality for comp cost; fairness between cards comes from equal fidelity in each card's own grammar, one surface, one aspect, never from shared unfinishedness. The frame's aspect is the surface's own: a native app or mobile-first surface comps portrait at its device viewport, a desktop web surface landscape, and the decision page adapts to either, so a phone screen comped landscape is a broken frame, not a neutral default. Produce in the order the user reads, the assigned card, then the pick, then the full-card hand, then canon, each file written with its prompt sidecar the moment it is done, so a re-roll's spend front-loads onto the cards read first; declined challengers get no comp, their catalog thumb is their face. When the harness runs subagents in parallel, fan the set out as one agent per card: each spawn is the shipped asset producer with a single-comp packet, that card's fields, PRODUCT.md, the shared frame, and the card's declared path, up to four in flight at once. A slot still empty when its agent returns is regenerated inline, and a slot still empty when the user answers is dropped without ceremony; no other supervision is owed. Without parallel subagents, generate in the main thread after serving, in the same reading order, and let the harness's own generation display carry the progress; the wait for the answer follows the last file. The chosen card's comp is not spent by the choice: on a comp-led build it enters the comp round as compositional option one, and on a code-led build it returns at the finish review as the critique reference, what the image dared that the build did not. The unchosen comps stay in `.impeccable/mocks/decision/` as the round's spent hand; they carry no approval and imply none. With no image generation, the cards carry their identity in palette chips and facts, and that page is complete, not a lesser version; the page then also demotes every challenger's catalog art to a labeled thumbnail on its own, because salience must encode the verdict, never the accident of which cards have images. The moment the direction lands, one more round on the same open table decides the execution contract. The direction payload declares `followup: true`, so the table stays open after the pick; deliver the build-path payload through `--update` immediately. Two text-only cards. **Comp-led**: a first-viewport comp is generated and it is law, the finish review audits the build against it; boldest composition on the table, fix rounds expected, motion at risk; choosing it makes the comp non-optional, no silent skipping. **Code-led**: no comp of this page and no apology for it; the QUALITY BAR boards still calibrate finish, and the ambition moves into the written contract, the FIRST VIEWPORT block plus a named signature interaction and motion grammar, which the finish reviewer audits in behavior; code-led is not a discount on commitment, the direction still lands fully committed in code. Lead with the chosen world's fit: a costume-heavy catalog world leads comp-led, a quiet or conventional direction leads code-led; the lead is a default, never a decision, and the user flips it freely. A standing preference, voiced once, is recorded as a brand commitment in PRODUCT.md and skips this round on later surfaces. Without image generation there is no fork and no round: code-led is the only path, stated in one line rather than asked. Only a detached table (`--start`) stays open for `--update`: a blocking serve or the structured-tool channel runs the build-path round as its own second question instead, and `followup: true` belongs only on a detached round. diff --git a/skill/reference/visualize.md b/skill/reference/visualize.md index 09dc93182..a1c023ce6 100644 --- a/skill/reference/visualize.md +++ b/skill/reference/visualize.md @@ -9,6 +9,9 @@ The purpose of a probe is to test composition, narrative, hierarchy, density, fo Render three distinct high-fidelity north-star comps of the requested surface, with whatever generation capability exists, saved under `.impeccable/mocks/` so they survive the session. Comp at the surface's own viewport: portrait at device size for a native app or mobile-first surface, desktop landscape otherwise; a phone screen comped landscape misstates the composition before anything gets built against it. Comps are the build thread's own work, never delegated: the thread that writes the comp prompts holds the direction's full context, and it has already seen every comp when the build starts. Open every image you produce or reference by its workspace-relative path, never an absolute one: sandboxed viewers reject absolute paths, and everything under the project root has a relative path. Base them on the real content and the surface concepts already developed with the user. Three is the number: one comp invites rubber-stamping, and the spread between three is what surfaces the composition worth building. The chosen card's decision comp is the first of the three: it already renders this direction at full fidelity under this file's discipline, so this round generates two more that vary what the first held fixed, and all three go to the approval point together. Only a round that arrives with no decision comp, a degraded roll, an identity-mode page, a direction pinned without the decision round, renders all three here. - A comp is a designed surface, not a picture of the subject. Lead the generation prompt with the surface's own structure, whatever regions this design actually has, named in order with their scale relationships; a page with no navigation states that instead of inventing one, and an unconventional surface states its unconventional skeleton. A prompt that leads with the world's atmosphere gets a vignette back: the model paints the fish market instead of the fish market's website. Self-check every render: if it could hang as a poster, or reads as a photograph or scene with some text on it, it is not a comp; regenerate with the layout scaffold stated more literally. +- The inverse failure fails too: a surface with none of its subject in it. The subject appears as the content the regions exist to hold: a forum about machines shows the machines, a menu shows the dishes, a player shows the work it plays, a dashboard shows the live data it watches. The world dresses the frame and never displaces what the frame exists to show. Self-check every render: point at the subject; a render that depicts everything about the world and nothing of the subject fails however faithful its atmosphere, so regenerate with the subject's imagery named region by region in the prompt. +- A comp is judged as the shipped screen, in the surface's mode and on its platform. The visitor's job must be readable from the image alone: an Operate surface shows the task's real controls and data mid-use, a Read surface shows its text at reading scale, a Persuade surface shows what is offered and the action that accepts it, an Experience surface gives the work itself the room. Self-check every render: name the mode from the image with no caption; a render whose mode cannot be read back is art direction without a surface, so regenerate with the visitor's job restated as the prompt's spine. +- Commitment is depth, not coverage. The world enters through one dominant move plus the material, type, and spacing that support it, and the remaining regions hold still so that move can be read; a region that simply does its job in the world's own grammar carries the direction further than a region performing the concept. Density is a mode decision, not a fidelity setting: calm is what Operate and Read surfaces are for, and even a Persuade or Experience surface spends its boldness on a focal moment rather than everywhere at once. Self-check every render: count what competes with the focal moment for the eye in the first viewport; a second contender means the comp is shouting, so regenerate keeping the strongest move and quieting the rest. Busy is louder, not bolder. - When the user shortlisted multiple concepts, spread the three across them. - When one direction is committed, vary the structural uncertainty an image can resolve: topology, sequence, density, hierarchy, focal composition, or interaction framing. - Show enough beyond the opening moment to prove the concept can govern the whole requested surface. From 248a4a699a0effa59c8385078dd6e000e99f076f Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Tue, 11 Aug 2026 15:53:25 -0400 Subject: [PATCH 2/6] Retire the sketch era's wire name: the field is comp, sketch is an alias The deliverable died in #545; the word survived as the decision-page payload's field name, annotated everywhere it appeared with the same compatibility apology. The page and the skill text ship together and payloads are per-session, so the compatibility burden is one input alias, not a frozen name. serve-question.mjs: the card field, the answer key, the schema docs, the --schema example, the help text, and every internal identifier (compSrc, data-comp, .media.comp-pending, img.comp, comp-note) now say comp; a payload declaring the legacy sketch key still renders and answers identically. new-work.md and the asset producer drop their wire-name parentheticals. The unit suite covers the canonical answer key coming back from a legacy-key payload; the new-work e2e's declined-card stray comp stays declared as sketch, which doubles as alias coverage. AI-assisted (Claude Fable 5), prepared for maintainer review. Co-Authored-By: Claude Fable 5 --- skill/agents/impeccable-asset-producer.md | 2 +- skill/reference/new-work.md | 2 +- skill/scripts/serve-question.mjs | 104 +++++++++++----------- tests/new-work-e2e.test.mjs | 5 +- tests/serve-question.test.mjs | 27 +++--- 5 files changed, 72 insertions(+), 68 deletions(-) diff --git a/skill/agents/impeccable-asset-producer.md b/skill/agents/impeccable-asset-producer.md index 6af0ab8ea..d10882f34 100644 --- a/skill/agents/impeccable-asset-producer.md +++ b/skill/agents/impeccable-asset-producer.md @@ -24,7 +24,7 @@ Do not redesign. Preserve the reference's visual role, silhouette, palette, ligh ## Decision Comps -When the parent hands you a decision card packet instead of an approved mock, the job is one comp: one card, one file, written to the card's declared `sketch` path (the field keeps its wire name) the moment it renders. The parent runs several of you in parallel, one per card, so your entire contract is this card; generate first, plan never, because the file on disk is the deliverable and the decision page is waiting on it. Work from the card's structured fields and PRODUCT.md alone; a card too thin to brief a comp is reported back, not padded from imagination. Render the card's direction as a north-star comp at full fidelity: the requested surface's first viewport, prompt led by the surface's own structure (its regions named in order with their scale relationships, never the world's atmosphere), fully committed in the card's own palette, type character, and material world; a native app or mobile-first surface is a portrait frame at its device viewport, never a landscape default. Every sibling renders at the same full fidelity in its own grammar, one surface, one aspect; equal commitment is what keeps the comparison honest. Real product name and real content only; never invent commercial claims, prices, benchmarks, or dates PRODUCT.md does not carry. Write the prompt sidecar beside the file. Return one line naming the path and any deviation, nothing more. Everything below this section is the asset-production job; none of it applies to a decision-comp run. +When the parent hands you a decision card packet instead of an approved mock, the job is one comp: one card, one file, written to the card's declared `comp` path the moment it renders. The parent runs several of you in parallel, one per card, so your entire contract is this card; generate first, plan never, because the file on disk is the deliverable and the decision page is waiting on it. Work from the card's structured fields and PRODUCT.md alone; a card too thin to brief a comp is reported back, not padded from imagination. Render the card's direction as a north-star comp at full fidelity: the requested surface's first viewport, prompt led by the surface's own structure (its regions named in order with their scale relationships, never the world's atmosphere), fully committed in the card's own palette, type character, and material world; a native app or mobile-first surface is a portrait frame at its device viewport, never a landscape default. Every sibling renders at the same full fidelity in its own grammar, one surface, one aspect; equal commitment is what keeps the comparison honest. Real product name and real content only; never invent commercial claims, prices, benchmarks, or dates PRODUCT.md does not carry. Write the prompt sidecar beside the file. Return one line naming the path and any deviation, nothing more. Everything below this section is the asset-production job; none of it applies to a decision-comp run. ## Input Contract diff --git a/skill/reference/new-work.md b/skill/reference/new-work.md index 343e2634f..ef3c60170 100644 --- a/skill/reference/new-work.md +++ b/skill/reference/new-work.md @@ -48,7 +48,7 @@ The script assigns which structure gets built; your top-ranked structure is what The standing exit: every direction round offers one quiet, permanent alternative, the category standard, played straight. It is the user's door, never yours: never recommend it, never weigh it against the roll, never let it soften the dealt directions; the counterweights bind the unchosen default, not the chosen one. When the user takes it, in the canon action, a safer-steer, or plain words asking for the familiar or competitor-like path, convention becomes the commitment: ask once for two or three products this should sit alongside, make their craft level the bar, and execute the canon at full fidelity, without irony or smuggled quirk. A standing preference gets recorded as a brand commitment in PRODUCT.md. Re-roll eliminates every direction already shown, grounded and challenger alike; after two consecutive re-rolls, ask what quality is missing. You may re-roll on your own only on named factual grounds, when the assigned direction cannot carry the product's truth or task; taste is never grounds. The user may re-roll freely, and a user- or brief-pinned direction beats the roll, always. Present the decision visually: write an options payload with the assigned direction leading and its raised lines included, the pick card when one exists, the dealt challengers as alternates carrying their QUALITY BAR cards plus each challenger's verdict and kept line, re-roll with its safer and bolder registers, steer, plus canon enabled, and `followup: true` when the execution-contract round will follow (it does whenever image generation exists and no standing build-path preference is recorded); a degraded roll with no challengers still uses the page, as a single text-only card with re-roll. Give every card the same anatomy, thesis, palette, materials, first viewport, honest risk, and the challengers' case lines (run the script with `--schema` for the exact shape); the page renders identity from these fields, routes declined challengers to a demoted row on its own, and a challenger's catalog image rides as labeled inspiration, never as the promise of the build. Author `canonCard` too: the category standard as one honest card with the same anatomy; the page keeps it subordinate, and the counterweights still bind you. Run `node {{scripts_path}}/serve-question.mjs --start --payload ` (run it with `--schema` first for the exact payload shape). It daemonizes, prints the page URL and a key, and exits immediately; now open that URL for the user, in-app browser first, then the system opener, then showing the URL. Collect the choice with `--wait --key `, repeating while it exits 3; the ANSWER prints as JSON. Exit 4 means the page was closed without an answer: re-present once through the structured question tool, and with no answer there either, proceed unattended with the assigned direction and state the assumptions. A harness that can leave a shell blocked in the background may instead run the script without `--start` and let it auto-open and block. Only a session where no browser can open at all, headless, CI, an eval worker, a remote shell with no display, puts the same decision through the structured question tool instead; the script self-detects these environments and exits 2 with that advice, so treat exit 2 as this fallback, never as an error to retry. -When image generation exists, every card also declares a `sketch` path under `.impeccable/mocks/decision/` (the field keeps its wire name for compatibility; what it carries is the card's comp), the canon card included. Where the harness sandboxes its shell, start the page through the least-sandboxed command path it offers: a sandboxed shell cannot bind the board's port, and the first-attempt failure costs a retry every session. Serve the page first, then produce the comps; the page shimmer-waits per slot and the user may answer before they land. Each card's image is that direction's north-star comp at full fidelity, produced under the comp discipline in [visualize.md](visualize.md): the requested surface's first viewport, structure-led prompt, real product name and real content, no invented commercial claims, in that card's own palette, type character, and material world, committed all the way, where commitment is depth, not coverage: visualize.md's self-checks, the mode readable from the image, one dominant move, the subject present as content, bind decision comps identically. Generation takes the same time at any fidelity, so an unfinished sketch pays sketch quality for comp cost; fairness between cards comes from equal fidelity in each card's own grammar, one surface, one aspect, never from shared unfinishedness. The frame's aspect is the surface's own: a native app or mobile-first surface comps portrait at its device viewport, a desktop web surface landscape, and the decision page adapts to either, so a phone screen comped landscape is a broken frame, not a neutral default. Produce in the order the user reads, the assigned card, then the pick, then the full-card hand, then canon, each file written with its prompt sidecar the moment it is done, so a re-roll's spend front-loads onto the cards read first; declined challengers get no comp, their catalog thumb is their face. When the harness runs subagents in parallel, fan the set out as one agent per card: each spawn is the shipped asset producer with a single-comp packet, that card's fields, PRODUCT.md, the shared frame, and the card's declared path, up to four in flight at once. A slot still empty when its agent returns is regenerated inline, and a slot still empty when the user answers is dropped without ceremony; no other supervision is owed. Without parallel subagents, generate in the main thread after serving, in the same reading order, and let the harness's own generation display carry the progress; the wait for the answer follows the last file. The chosen card's comp is not spent by the choice: on a comp-led build it enters the comp round as compositional option one, and on a code-led build it returns at the finish review as the critique reference, what the image dared that the build did not. The unchosen comps stay in `.impeccable/mocks/decision/` as the round's spent hand; they carry no approval and imply none. With no image generation, the cards carry their identity in palette chips and facts, and that page is complete, not a lesser version; the page then also demotes every challenger's catalog art to a labeled thumbnail on its own, because salience must encode the verdict, never the accident of which cards have images. +When image generation exists, every card also declares a `comp` path under `.impeccable/mocks/decision/`, the canon card included. Where the harness sandboxes its shell, start the page through the least-sandboxed command path it offers: a sandboxed shell cannot bind the board's port, and the first-attempt failure costs a retry every session. Serve the page first, then produce the comps; the page shimmer-waits per slot and the user may answer before they land. Each card's image is that direction's north-star comp at full fidelity, produced under the comp discipline in [visualize.md](visualize.md): the requested surface's first viewport, structure-led prompt, real product name and real content, no invented commercial claims, in that card's own palette, type character, and material world, committed all the way, where commitment is depth, not coverage: visualize.md's self-checks, the mode readable from the image, one dominant move, the subject present as content, bind decision comps identically. Generation takes the same time at any fidelity, so an unfinished draft pays draft quality for comp cost; fairness between cards comes from equal fidelity in each card's own grammar, one surface, one aspect, never from shared unfinishedness. The frame's aspect is the surface's own: a native app or mobile-first surface comps portrait at its device viewport, a desktop web surface landscape, and the decision page adapts to either, so a phone screen comped landscape is a broken frame, not a neutral default. Produce in the order the user reads, the assigned card, then the pick, then the full-card hand, then canon, each file written with its prompt sidecar the moment it is done, so a re-roll's spend front-loads onto the cards read first; declined challengers get no comp, their catalog thumb is their face. When the harness runs subagents in parallel, fan the set out as one agent per card: each spawn is the shipped asset producer with a single-comp packet, that card's fields, PRODUCT.md, the shared frame, and the card's declared path, up to four in flight at once. A slot still empty when its agent returns is regenerated inline, and a slot still empty when the user answers is dropped without ceremony; no other supervision is owed. Without parallel subagents, generate in the main thread after serving, in the same reading order, and let the harness's own generation display carry the progress; the wait for the answer follows the last file. The chosen card's comp is not spent by the choice: on a comp-led build it enters the comp round as compositional option one, and on a code-led build it returns at the finish review as the critique reference, what the image dared that the build did not. The unchosen comps stay in `.impeccable/mocks/decision/` as the round's spent hand; they carry no approval and imply none. With no image generation, the cards carry their identity in palette chips and facts, and that page is complete, not a lesser version; the page then also demotes every challenger's catalog art to a labeled thumbnail on its own, because salience must encode the verdict, never the accident of which cards have images. The moment the direction lands, one more round on the same open table decides the execution contract. The direction payload declares `followup: true`, so the table stays open after the pick; deliver the build-path payload through `--update` immediately. Two text-only cards. **Comp-led**: a first-viewport comp is generated and it is law, the finish review audits the build against it; boldest composition on the table, fix rounds expected, motion at risk; choosing it makes the comp non-optional, no silent skipping. **Code-led**: no comp of this page and no apology for it; the QUALITY BAR boards still calibrate finish, and the ambition moves into the written contract, the FIRST VIEWPORT block plus a named signature interaction and motion grammar, which the finish reviewer audits in behavior; code-led is not a discount on commitment, the direction still lands fully committed in code. Lead with the chosen world's fit: a costume-heavy catalog world leads comp-led, a quiet or conventional direction leads code-led; the lead is a default, never a decision, and the user flips it freely. A standing preference, voiced once, is recorded as a brand commitment in PRODUCT.md and skips this round on later surfaces. Without image generation there is no fork and no round: code-led is the only path, stated in one line rather than asked. Only a detached table (`--start`) stays open for `--update`: a blocking serve or the structured-tool channel runs the build-path round as its own second question instead, and `followup: true` belongs only on a detached round. diff --git a/skill/scripts/serve-question.mjs b/skill/scripts/serve-question.mjs index 788211920..09693e2ef 100644 --- a/skill/scripts/serve-question.mjs +++ b/skill/scripts/serve-question.mjs @@ -42,14 +42,14 @@ * // raise lines under the identity row * "risk": "one line: the honest risk", // optional * "body": "fallback prose when the structured fields are absent", - * "sketch": ".impeccable/mocks/decision/assigned.webp", // optional; the card's - * // full-fidelity direction comp (the field - * // keeps the sketch era's wire name). May not - * // exist yet: the page shimmer-waits and + * "comp": ".impeccable/mocks/decision/assigned.webp", // optional; the card's + * // full-fidelity direction comp (the legacy + * // key "sketch" is accepted as an alias). May + * // not exist yet: the page shimmer-waits and * // polls the slot until the file lands, so * // serve first and generate after * "hero": "https://... or /abs/path.webp", // optional inspiration image; - * // rides picture-in-picture when a sketch exists + * // rides picture-in-picture when a comp exists * "board": "https://... or /abs/path.webp" // optional secondary image * }, ... * ], @@ -61,7 +61,7 @@ * "canon": true, // adds the "Play it straight" standing exit; * // direction rounds only (returns {"optionId":"canon"}) * "canonCard": { ... }, // optional: the standing exit as a full card with the - * // same anatomy (label, thesis, palette, sketch, ...); + * // same anatomy (label, thesis, palette, comp, ...); * // rendered last and visually subordinate. Without it, * // canon stays a quiet footer action. * "steer": true, // adds a free-text steer field returned with any answer @@ -75,7 +75,7 @@ * // then the execution contract. * } * - * Options render as large cards: the sketch leads when present, with the + * Options render as large cards: the comp leads when present, with the * inspiration image picture-in-picture; a hero alone renders full-bleed; a * text-only direction gets its identity from the palette chips and tags. * Local image paths are served by this server; nothing is uploaded anywhere. @@ -148,7 +148,7 @@ function printAnswer(raw) { if (a.hero || a.board) { console.log("CHOSEN CARD: open the chosen world's board and hero images now, before any code. When your harness only reads files, or runs sandboxed, download them INTO the workspace and open the relative path; a sandboxed viewer rejects absolute paths outside it. They set the craft bar the build must reach."); } - if (a.sketch) { + if (a.comp) { console.log('CHOSEN COMP: the decision comp at that path is compositional option one. On a comp-led build the comp round adds two variations beside it; on a code-led build it returns at the finish review as the critique reference. Never regenerate it from scratch.'); } if (a.optionId === 'canon') { @@ -175,17 +175,17 @@ if (hasFlag('schema')) { title: 'Choose the visual world', question: 'The roll assigned Fillmore Handbill. Keep it, take an alternate, or re-roll.', options: [ - { id: 'assigned', label: 'Fillmore Handbill', kicker: 'THE ROLL', lineage: '1966-71 Fillmore psychedelic handbills', thesis: 'The gig poster that treats every release like a one-night stand.', palette: ['#e8452c', '#f5d64c', '#1b2a52', '#f3ead8'], materials: ['letterpress', 'split-fountain ink'], viewport: 'A full-bleed dated bill with the product name in warped display type.', risk: 'Reads nostalgic when the type is set timidly.', raised: [{ from: 'challenger-microfiche', raise: 'The bill now owns its whole viewport as one continuous printed sheet.' }], sketch: '.impeccable/mocks/decision/assigned.webp', hero: 'https://impeccable.style/worlds/cards/posters-covers-sleeves-fillmore-handbill-hero.webp', board: 'https://impeccable.style/worlds/cards/posters-covers-sleeves-fillmore-handbill.webp' }, - { id: 'model-pick', label: 'The Broadside Ballad', kicker: 'MY PICK', lineage: 'street-sold ballad sheets', thesis: 'Every release printed as the day’s ballad sheet.', palette: ['#1f1c18', '#efe5d0', '#a33327'], materials: ['woodcut', 'rag paper'], viewport: 'One tall sheet, the newest release as today’s ballad.', risk: 'Also the direction most runs in this category land on.', sketch: '.impeccable/mocks/decision/model-pick.webp' }, - { id: 'challenger-teletext', label: 'Teletext Service', verdict: 'competitive', lineage: 'broadcast teletext magazines', thesis: 'The catalog as a broadcast index: pages, not sections.', palette: ['#0000c0', '#ffff00', '#00c000', '#ffffff'], materials: ['block mosaic', 'phosphor glow'], viewport: 'P100 index page, releases as numbered rows.', case: 'Fuses cleanly: releases map to numbered pages; loses narrowly on clarity.', risk: 'Reads retro-novelty when the grid is not strict.', sketch: '.impeccable/mocks/decision/challenger-teletext.webp', hero: 'https://impeccable.style/worlds/cards/broadcast-programming-teletext-service-hero.webp' }, + { id: 'assigned', label: 'Fillmore Handbill', kicker: 'THE ROLL', lineage: '1966-71 Fillmore psychedelic handbills', thesis: 'The gig poster that treats every release like a one-night stand.', palette: ['#e8452c', '#f5d64c', '#1b2a52', '#f3ead8'], materials: ['letterpress', 'split-fountain ink'], viewport: 'A full-bleed dated bill with the product name in warped display type.', risk: 'Reads nostalgic when the type is set timidly.', raised: [{ from: 'challenger-microfiche', raise: 'The bill now owns its whole viewport as one continuous printed sheet.' }], comp: '.impeccable/mocks/decision/assigned.webp', hero: 'https://impeccable.style/worlds/cards/posters-covers-sleeves-fillmore-handbill-hero.webp', board: 'https://impeccable.style/worlds/cards/posters-covers-sleeves-fillmore-handbill.webp' }, + { id: 'model-pick', label: 'The Broadside Ballad', kicker: 'MY PICK', lineage: 'street-sold ballad sheets', thesis: 'Every release printed as the day’s ballad sheet.', palette: ['#1f1c18', '#efe5d0', '#a33327'], materials: ['woodcut', 'rag paper'], viewport: 'One tall sheet, the newest release as today’s ballad.', risk: 'Also the direction most runs in this category land on.', comp: '.impeccable/mocks/decision/model-pick.webp' }, + { id: 'challenger-teletext', label: 'Teletext Service', verdict: 'competitive', lineage: 'broadcast teletext magazines', thesis: 'The catalog as a broadcast index: pages, not sections.', palette: ['#0000c0', '#ffff00', '#00c000', '#ffffff'], materials: ['block mosaic', 'phosphor glow'], viewport: 'P100 index page, releases as numbered rows.', case: 'Fuses cleanly: releases map to numbered pages; loses narrowly on clarity.', risk: 'Reads retro-novelty when the grid is not strict.', comp: '.impeccable/mocks/decision/challenger-teletext.webp', hero: 'https://impeccable.style/worlds/cards/broadcast-programming-teletext-service-hero.webp' }, { id: 'challenger-microfiche', label: 'Microfiche Reader', verdict: 'declined', lineage: 'library microfiche stations', palette: ['#101418', '#9fb4c0'], materials: ['film grain', 'backlit glass'], case: 'Fuses poorly: listeners do not identify with archival retrieval.', kept: 'Total environmental commitment.', hero: 'https://impeccable.style/worlds/cards/archives-microfiche-reader-hero.webp' }, ], reroll: { registers: ['safer', 'bolder'] }, canon: true, - canonCard: { label: 'The category standard', thesis: 'What this category ships, executed impeccably.', palette: ['#ffffff', '#111827', '#2563eb'], materials: ['clean grid', 'product photography'], viewport: 'The arrangement a visitor expects, at full craft.', risk: 'Indistinguishable from the competition by design.', sketch: '.impeccable/mocks/decision/canon.webp' }, + canonCard: { label: 'The category standard', thesis: 'What this category ships, executed impeccably.', palette: ['#ffffff', '#111827', '#2563eb'], materials: ['clean grid', 'product photography'], viewport: 'The arrangement a visitor expects, at full craft.', risk: 'Indistinguishable from the competition by design.', comp: '.impeccable/mocks/decision/canon.webp' }, steer: true, }, null, 2)); - console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/sketch accept URLs or local paths; sketch slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the sketch, never as the promise of the build. verdict routes rendering: "wins" and "competitive" challengers keep full cards, "declined" ones render demoted after them (narrow, quiet, art as a labeled thumb, "Adopt anyway"), with their kept line on the front; the page reorders declined cards to the end on its own. raised on the assigned card renders each donation as a named raise line. Salience parity: when the assigned card declares no sketch (no image generation this round), catalog art on every card demotes to a labeled thumb, so what looks important is the verdict’s call, never rendering luck. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation. The pick card is a kicker convention, not a field: kicker "MY PICK" on your top-ranked grounded candidate, one at most, never in the lead slot. Every card gets the full anatomy, challengers, canon, and declined included: thesis, palette, materials, viewport, risk; the seed already hands you each challenger’s system rules, so a card with no palette chips is an authoring gap, not a data gap. Keep thesis and each fact to one short sentence: the card front shows thesis, identity, and a two-line risk, while first viewport and the case read on the card back behind the Details chip, so long facts cost the reader a flip, not the page its scanability. A card with no imagery at all has no back; its full read renders on the front, so a text-only round loses nothing. The sketch slot carries the card’s full-fidelity direction comp (the field keeps its wire name for compatibility). Comp aspect follows the surface: portrait at device viewport for native or mobile-first surfaces, landscape otherwise; the page adapts its cards to either. reroll accepts true or { "registers": ["safer", "bolder"] }: the register buttons steer the next hand along the familiar-to-bold axis, the answer carries "register", and you re-run concept-seed with --register for the next round; offer the registers on direction rounds, and never pre-select one. followup: true keeps the table open after a pick for a second round via --update (direction first, then the execution contract); send the next payload immediately, the page is waiting on it.'); + console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board/comp accept URLs or local paths; comp slots may point at files that do not exist yet (serve first, generate after; the page polls until they land, so never block serving on generation). hero on a challenger is the inspiration it draws from and renders picture-in-picture beside the comp, never as the promise of the build. verdict routes rendering: "wins" and "competitive" challengers keep full cards, "declined" ones render demoted after them (narrow, quiet, art as a labeled thumb, "Adopt anyway"), with their kept line on the front; the page reorders declined cards to the end on its own. raised on the assigned card renders each donation as a named raise line. Salience parity: when the assigned card declares no comp (no image generation this round), catalog art on every card demotes to a labeled thumb, so what looks important is the verdict’s call, never rendering luck. canonCard renders the standing exit as a subordinate card with the same anatomy; without it, canon stays a quiet footer action. Include canon only for visual-direction rounds; never present it as your own recommendation. The pick card is a kicker convention, not a field: kicker "MY PICK" on your top-ranked grounded candidate, one at most, never in the lead slot. Every card gets the full anatomy, challengers, canon, and declined included: thesis, palette, materials, viewport, risk; the seed already hands you each challenger’s system rules, so a card with no palette chips is an authoring gap, not a data gap. Keep thesis and each fact to one short sentence: the card front shows thesis, identity, and a two-line risk, while first viewport and the case read on the card back behind the Details chip, so long facts cost the reader a flip, not the page its scanability. A card with no imagery at all has no back; its full read renders on the front, so a text-only round loses nothing. The comp slot carries the card’s full-fidelity direction comp (the legacy key "sketch" is accepted as an alias). Comp aspect follows the surface: portrait at device viewport for native or mobile-first surfaces, landscape otherwise; the page adapts its cards to either. reroll accepts true or { "registers": ["safer", "bolder"] }: the register buttons steer the next hand along the familiar-to-bold axis, the answer carries "register", and you re-run concept-seed with --register for the next round; offer the registers on direction rounds, and never pre-select one. followup: true keeps the table open after a pick for a second round via --update (direction first, then the execution contract); send the next payload immediately, the page is waiting on it.'); process.exit(0); } @@ -322,10 +322,10 @@ function loadRound(json) { localImages.push(abs); return `/img/${localImages.length - 1}`; }; - // Sketches stream in after the page is served, so their slots register + // Comps stream in after the page is served, so their slots register // whether or not the file exists yet; /img answers 404 until it lands and - // the page polls the slot. Remote sketch URLs pass through untouched. - const sketchSrc = (value) => { + // the page polls the slot. Remote comp URLs pass through untouched. + const compSrc = (value) => { if (!value) return null; if (/^https?:\/\//.test(value)) return value; localImages.push(path.resolve(value)); @@ -336,7 +336,7 @@ function loadRound(json) { ...option, heroSrc: imageSrc(option.hero), boardSrc: imageSrc(option.board), - sketchSrc: sketchSrc(option.sketch), + compSrc: compSrc(option.comp ?? option.sketch), }); options = parsed.options.map(decorate); // The verdict routes rendering: full cards first, then the canon, then the @@ -374,12 +374,12 @@ function page() { // assigned card would let rendering luck outvote the weighing: users click // the colorful thing. Declined cards are thumb-only regardless; the verdict // demoted them, and a full-bleed hero would promote them right back. - const identityRound = !(options[0] && (options[0].sketchSrc || options[0].heroSrc || options[0].boardSrc)); - // A declined card never renders a full media face, sketch included: even a - // declared sketch would buy back the salience the verdict took away. - const faceSketch = (option) => demoted(option) ? null : option.sketchSrc; - const thumbOnly = (option) => !faceSketch(option) && Boolean(option.heroSrc || option.boardSrc) && (demoted(option) || identityRound); - const hasMedia = (option) => Boolean(faceSketch(option) || ((option.heroSrc || option.boardSrc) && !thumbOnly(option))); + const identityRound = !(options[0] && (options[0].compSrc || options[0].heroSrc || options[0].boardSrc)); + // A declined card never renders a full media face, comp included: even a + // declared comp would buy back the salience the verdict took away. + const faceComp = (option) => demoted(option) ? null : option.compSrc; + const thumbOnly = (option) => !faceComp(option) && Boolean(option.heroSrc || option.boardSrc) && (demoted(option) || identityRound); + const hasMedia = (option) => Boolean(faceComp(option) || ((option.heroSrc || option.boardSrc) && !thumbOnly(option))); // The back exists to keep long facts off a card whose front is an image; // a card with no art has no flip chip to reach it, so it gets no back and // the full read lives on the front instead. @@ -421,7 +421,7 @@ function page() { } // The front carries only what the choice needs: thesis, identity, and the // honest risk clamped to two lines. First viewport and the case read on - // the card's back; once the sketch lands, the first viewport is a picture. + // the card's back; once the comp lands, the first viewport is a picture. // With no art there is no back, so the full read fills the room the // image would have taken. if (hasMedia(option)) { @@ -450,18 +450,18 @@ function page() { ` : ''; const details = hasBack(option) ? flipChip('Details') : ''; // Thumb-only art renders inside the body via anatomy(), never as a face, - // and a declined card's sketch slot is ignored outright. + // and a declined card's comp slot is ignored outright. if (thumbOnly(option)) return ''; - if (faceSketch(option)) { - return `
-
rendering…
- + if (faceComp(option)) { + return `
+
rendering…
+ ${inspiration}
${expandChip}${details}
`; } if (option.heroSrc || option.boardSrc) { - // Without a sketch the catalog art is the card's face; it stays a + // Without a comp the catalog art is the card's face; it stays a // labeled reference so it never reads as the promise of the build. return `
@@ -554,9 +554,9 @@ function page() { its axis with snap points and the arrows page it card by card. */ .grid { --deck-inset: max(clamp(1rem, 5vw, 4rem), calc((100vw - 90rem) / 2)); display: flex; gap: 1.6rem; width: 100%; overflow-x: auto; overflow-y: hidden; scroll-snap-type: x mandatory; scrollbar-width: none; padding: 6px var(--deck-inset); scroll-padding-inline: var(--deck-inset); align-items: stretch; } .grid::-webkit-scrollbar { display: none; } - /* Wide enough that the sketch carries the card: at 27vw the imagery read + /* Wide enough that the comp carries the card: at 27vw the imagery read as a thumbnail above a column of copy, and the copy won the attention - contest the sketch is supposed to win. */ + contest the comp is supposed to win. */ .grid > .card { flex: 0 0 clamp(24rem, 34vw, 34rem); scroll-snap-align: center; } .nav { position: absolute; z-index: 6; width: 42px; height: 42px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: oklch(7% 0.006 95 / 0.78); border: 1px solid var(--ks-rule); color: var(--ks-kinpaku); cursor: pointer; backdrop-filter: blur(6px); transition: border-color .2s, color .2s, opacity .2s; } .nav:hover { border-color: var(--ks-kinpaku-deep); color: var(--ks-kinpaku-pale); } @@ -611,7 +611,7 @@ function page() { region entirely instead of reserving a blank 16:9 void. */ .face.text-only .kicker { position: static; align-self: flex-start; margin: 14px 0 0 14px; } .face.text-only .body { padding-top: 12px; } - /* 16/10 matches the landscape sketch frame; portrait art overrides the + /* 16/10 matches the landscape comp frame; portrait art overrides the slot with its own exact ratio at load (see the load listener), and the deck narrows so portrait cards line up side by side. */ .media { position: relative; width: 100%; aspect-ratio: 16/10; flex: none; } @@ -647,14 +647,14 @@ function page() { .body.back-body { overflow-y: auto; flex: 1; scrollbar-width: thin; } /* Inspiration rides picture-in-picture: the catalog world explains where the direction comes from without promising what the build will look like. */ - /* Hovering the inspiration takes over the whole media region; the sketch is + /* Hovering the inspiration takes over the whole media region; the comp is the promise, the inspiration is a glance, so the glance must cost nothing. */ .pip { position: absolute; z-index: 2; left: 10px; bottom: 10px; margin: 0; width: 84px; height: 64px; border: 1px solid var(--ks-rule); border-radius: 6px; overflow: hidden; background: var(--ks-lacquer); cursor: zoom-in; transition: left .35s cubic-bezier(.16,1,.3,1), bottom .35s cubic-bezier(.16,1,.3,1), width .35s cubic-bezier(.16,1,.3,1), height .35s cubic-bezier(.16,1,.3,1), border-radius .35s ease; box-shadow: 0 6px 18px oklch(0% 0 0 / 0.45); } .pip img { display: block; width: 100%; height: 100%; object-fit: cover; } .pip figcaption { position: absolute; left: 0; right: 0; bottom: 0; font-family: var(--ks-mono); font-size: .5rem; letter-spacing: .2em; text-transform: uppercase; color: var(--ks-text); text-align: center; padding: 3px 0 4px; background: oklch(7% 0.006 95 / 0.72); backdrop-filter: blur(3px); } .pip:hover { left: 0; bottom: 0; width: 100%; height: 100%; border-radius: 0; z-index: 3; } - .sketch-note { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-family: var(--ks-mono); font-size: .66rem; letter-spacing: .22em; text-transform: uppercase; color: var(--ks-text-faint); } - /* Catalog art standing in for a sketchless card is a reference, and says so + .comp-note { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-family: var(--ks-mono); font-size: .66rem; letter-spacing: .22em; text-transform: uppercase; color: var(--ks-text-faint); } + /* Catalog art standing in for a comp-less card is a reference, and says so on its face; the same pill later carries "artwork unavailable". */ .media-label { position: absolute; z-index: 2; left: 10px; bottom: 10px; margin: 0; font-family: var(--ks-mono); font-size: .5rem; letter-spacing: .2em; text-transform: uppercase; color: var(--ks-text); padding: 3px 8px 4px; background: oklch(7% 0.006 95 / 0.72); border: 1px solid var(--ks-rule); border-radius: 4px; backdrop-filter: blur(3px); } /* Art that never arrives collapses to the card's own palette (painted @@ -666,15 +666,15 @@ function page() { .media.unavailable::after { content: ""; position: absolute; inset: 0; z-index: 1; background: oklch(10% 0.008 95 / 0.45); pointer-events: none; } .media.unavailable .chips { z-index: 2; } /* A stand-in is honest about being one: dimmed, labeled, and replaced by - the real sketch whenever it lands. */ - .media.stand-in img.sketch { filter: brightness(.72) saturate(.85); } + the real comp whenever it lands. */ + .media.stand-in img.comp { filter: brightness(.72) saturate(.85); } .media.stand-in .pip { display: none; } .stand-in-label { position: absolute; z-index: 2; left: 0; right: 0; bottom: 0; margin: 0; font-family: var(--ks-mono); font-size: .56rem; letter-spacing: .2em; text-transform: uppercase; color: var(--ks-text); text-align: center; padding: 4px 0 5px; background: oklch(7% 0.006 95 / 0.78); backdrop-filter: blur(3px); } - .media.sketching { position: relative; } - .media.sketching .shimmer { position: absolute; inset: 0; } - .media img.sketch { position: relative; z-index: 1; } + .media.comp-pending { position: relative; } + .media.comp-pending .shimmer { position: absolute; inset: 0; } + .media img.comp { position: relative; z-index: 1; } /* The generic .media img display:block would defeat [hidden] and float an - empty block over the shimmer; an unloaded sketch must truly not render. */ + empty block over the shimmer; an unloaded comp must truly not render. */ .media img[hidden] { display: none; } /* Declined challengers: the weighing demoted them, so the card is narrower and quieter, its catalog art rides as a labeled thumb in the body, and @@ -863,22 +863,22 @@ function page() { })); } - // Sketches stream in after the deal: poll each slot until the file lands, + // Comps stream in after the deal: poll each slot until the file lands, // then swap the shimmer for the image. Generation is genuinely slow and a // sequential batch puts the last card many minutes out, so patience is the // default: a slot only shows its inspiration as a stand-in when it has // waited four minutes AND nothing has landed anywhere for four minutes, the - // stand-in is labeled as such, and polling continues so the real sketch + // stand-in is labeled as such, and polling continues so the real comp // still swaps in whenever it arrives. Progress anywhere resets patience. const landTracker = { last: Date.now() }; - document.querySelectorAll('.media.sketching').forEach(m => { - const url = m.dataset.sketch; - const img = m.querySelector('img.sketch'); - const note = m.querySelector('.sketch-note'); + document.querySelectorAll('.media.comp-pending').forEach(m => { + const url = m.dataset.comp; + const img = m.querySelector('img.comp'); + const note = m.querySelector('.comp-note'); const started = Date.now(); // A live elapsed count is the difference between "working" and "frozen". const tick = setInterval(() => { if (note) note.textContent = 'rendering · ' + Math.round((Date.now() - started) / 1000) + 's'; }, 1000); - const settle = () => { clearInterval(tick); m.classList.remove('sketching', 'stand-in'); m.querySelector('.shimmer')?.remove(); m.querySelector('.stand-in-label')?.remove(); }; + const settle = () => { clearInterval(tick); m.classList.remove('comp-pending', 'stand-in'); m.querySelector('.shimmer')?.remove(); m.querySelector('.stand-in-label')?.remove(); }; const standIn = () => { const pip = m.querySelector('.pip img'); if (!pip || m.classList.contains('stand-in')) return; @@ -910,7 +910,7 @@ function page() { // slots are excluded; their polling owns the wait. const artFailed = (img) => { const m = img.closest('.media'); - if (!m || m.classList.contains('sketching') || m.classList.contains('unavailable')) return; + if (!m || m.classList.contains('comp-pending') || m.classList.contains('unavailable')) return; m.classList.add('unavailable'); const colors = [...(img.closest('.card')?.querySelectorAll('.swatches i') || [])].map(i => i.style.background).filter(Boolean); if (colors.length) m.style.background = 'linear-gradient(135deg, ' + colors.map((c, i) => c + ' ' + Math.round(i * 100 / colors.length) + '% ' + Math.round((i + 1) * 100 / colors.length) + '%').join(', ') + ')'; @@ -923,7 +923,7 @@ function page() { label.textContent = 'artwork unavailable'; m.appendChild(label); }; - document.querySelectorAll('.media:not(.sketching) > img').forEach(img => { + document.querySelectorAll('.media:not(.comp-pending) > img').forEach(img => { if (img.complete && img.naturalWidth === 0 && img.getAttribute('src')) artFailed(img); else img.addEventListener('error', () => artFailed(img), { once: true }); }); @@ -1129,7 +1129,7 @@ const server = http.createServer((req, res) => { ...(isReroll && (parsed.register === 'safer' || parsed.register === 'bolder') ? { register: parsed.register } : {}), ...(followupOpen ? { followup: true } : {}), ...(chosen?.hero || chosen?.board ? { hero: chosen.hero ?? null, board: chosen.board ?? null } : {}), - ...(chosen?.sketch ? { sketch: chosen.sketch } : {}), + ...((chosen?.comp ?? chosen?.sketch) ? { comp: chosen.comp ?? chosen.sketch } : {}), }); if (detachedKey) { fs.mkdirSync(QUESTION_DIR, { recursive: true }); diff --git a/tests/new-work-e2e.test.mjs b/tests/new-work-e2e.test.mjs index 4a3b546a5..461180159 100644 --- a/tests/new-work-e2e.test.mjs +++ b/tests/new-work-e2e.test.mjs @@ -333,8 +333,9 @@ describe('new-work-e2e: serve-question decision page', () => { id: 'challenger-deepsea', label: 'Deep Sea Survey', verdict: 'declined', case: 'Fuses poorly: buyers do not identify with abyssal instrumentation.', kept: 'Total environmental commitment.', hero, - // A stray sketch on a declined card must not re-promote it to a - // full media face; the renderer ignores it outright. + // A stray comp on a declined card must not re-promote it to a + // full media face; the renderer ignores it outright. Declared with + // the legacy `sketch` key, which doubles as alias coverage. sketch: '.impeccable/sketches/challenger-deepsea.webp', }, { id: 'challenger-waxprint', label: 'Wax Print Market', verdict: 'competitive', hero: winnerHero }, diff --git a/tests/serve-question.test.mjs b/tests/serve-question.test.mjs index 2a8e22f2d..279cff761 100644 --- a/tests/serve-question.test.mjs +++ b/tests/serve-question.test.mjs @@ -190,9 +190,9 @@ describe('serve-question', () => { assert.equal(dead, 2, 'a truly missing process must still read as gone'); }); - it('renders anatomy, streams late sketches, and returns the chosen sketch', async () => { + it('renders anatomy, streams late comps, and returns the chosen comp', async () => { const dir = mkdtempSync(path.join(tmpdir(), 'serve-question-')); - const sketchPath = path.join(dir, 'sketches', 'assigned.webp'); + const compPath = path.join(dir, 'comps', 'assigned.webp'); const payload = { title: 'Choose the visual world', options: [ @@ -200,7 +200,9 @@ describe('serve-question', () => { id: 'assigned', label: 'Fillmore Handbill', kicker: 'THE ROLL', thesis: 'The gig poster idea.', palette: ['#e8452c', '#f5d64c'], materials: ['letterpress'], viewport: 'Full-bleed dated bill.', risk: 'Nostalgia trap.', - sketch: sketchPath, hero: 'https://impeccable.style/worlds/cards/x-hero.webp', + // The legacy key: a payload authored against the sketch-era schema + // must keep rendering, so the lead card declares its comp as `sketch`. + sketch: compPath, hero: 'https://impeccable.style/worlds/cards/x-hero.webp', }, { id: 'challenger-1', label: 'Teletext Service', case: 'Fuses cleanly.' }, ], @@ -222,24 +224,25 @@ describe('serve-question', () => { assert.match(html, /class="tag">letterpress/); assert.match(html, /The gig poster idea\./); assert.match(html, /Fuses cleanly\./); - // The inspiration image rides picture-in-picture beside the sketch slot. + // The inspiration image rides picture-in-picture beside the comp slot. assert.match(html, /class="pip"/); - assert.match(html, /media sketching/); + assert.match(html, /media comp-pending/); // canonCard renders as a subordinate card and suppresses the footer action. assert.match(html, /card canon/); assert.match(html, /Play it straight { }); const code = await new Promise((resolve) => child.on('exit', resolve)); assert.equal(code, 0); - assert.match(read(), /"sketch":/); + assert.match(read(), /"comp":/); assert.match(read(), /CHOSEN COMP:/); }); }); From e867487d5548c443ca45df1498ad198109c15d45 Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Tue, 11 Aug 2026 16:09:21 -0400 Subject: [PATCH 3/6] Ban fabrications, never media: counter the exclusion-list reflex everywhere prompts are authored The declined moto-forum comp's prompt read 'no gradients, no rounded SaaS cards, no photography, no fake member counts, no badges, no testimonials': the reflex that rightly bans invented claims swallowed the one medium the subject lives in, and that is exactly how a motorcycle forum got comped with no motorcycles. The lektor prompt's 'no AI imagery', written by an image model, is the same fingerprint. One counterweight, phrased once per authoring surface: the comp discipline's subject-presence check (which the decision comps already bind), the asset producer's own prompt rules (a standalone agent that never reads visualize.md), and new-work's author-assets law (the path a code-led build takes without the comp round). Truth binds claims, not demonstrations; a photo of the subject doing its job is a demonstration. AI-assisted (Claude Fable 5), prepared for maintainer review. Co-Authored-By: Claude Fable 5 --- skill/agents/impeccable-asset-producer.md | 2 +- skill/reference/new-work.md | 2 +- skill/reference/visualize.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/skill/agents/impeccable-asset-producer.md b/skill/agents/impeccable-asset-producer.md index d10882f34..76aa5efc1 100644 --- a/skill/agents/impeccable-asset-producer.md +++ b/skill/agents/impeccable-asset-producer.md @@ -24,7 +24,7 @@ Do not redesign. Preserve the reference's visual role, silhouette, palette, ligh ## Decision Comps -When the parent hands you a decision card packet instead of an approved mock, the job is one comp: one card, one file, written to the card's declared `comp` path the moment it renders. The parent runs several of you in parallel, one per card, so your entire contract is this card; generate first, plan never, because the file on disk is the deliverable and the decision page is waiting on it. Work from the card's structured fields and PRODUCT.md alone; a card too thin to brief a comp is reported back, not padded from imagination. Render the card's direction as a north-star comp at full fidelity: the requested surface's first viewport, prompt led by the surface's own structure (its regions named in order with their scale relationships, never the world's atmosphere), fully committed in the card's own palette, type character, and material world; a native app or mobile-first surface is a portrait frame at its device viewport, never a landscape default. Every sibling renders at the same full fidelity in its own grammar, one surface, one aspect; equal commitment is what keeps the comparison honest. Real product name and real content only; never invent commercial claims, prices, benchmarks, or dates PRODUCT.md does not carry. Write the prompt sidecar beside the file. Return one line naming the path and any deviation, nothing more. Everything below this section is the asset-production job; none of it applies to a decision-comp run. +When the parent hands you a decision card packet instead of an approved mock, the job is one comp: one card, one file, written to the card's declared `comp` path the moment it renders. The parent runs several of you in parallel, one per card, so your entire contract is this card; generate first, plan never, because the file on disk is the deliverable and the decision page is waiting on it. Work from the card's structured fields and PRODUCT.md alone; a card too thin to brief a comp is reported back, not padded from imagination. Render the card's direction as a north-star comp at full fidelity: the requested surface's first viewport, prompt led by the surface's own structure (its regions named in order with their scale relationships, never the world's atmosphere), fully committed in the card's own palette, type character, and material world; a native app or mobile-first surface is a portrait frame at its device viewport, never a landscape default. Every sibling renders at the same full fidelity in its own grammar, one surface, one aspect; equal commitment is what keeps the comparison honest. Real product name and real content only; never invent commercial claims, prices, benchmarks, or dates PRODUCT.md does not carry. Your prompt's exclusion list bans fabrications, never media: a subject that lives in photographs keeps its photographs, and writing "no photography" for a photographic subject deletes the subject while the invented-claim risk stays untouched. Write the prompt sidecar beside the file. Return one line naming the path and any deviation, nothing more. Everything below this section is the asset-production job; none of it applies to a decision-comp run. ## Input Contract diff --git a/skill/reference/new-work.md b/skill/reference/new-work.md index ef3c60170..1bb3abaa2 100644 --- a/skill/reference/new-work.md +++ b/skill/reference/new-work.md @@ -97,7 +97,7 @@ Build the assigned direction, not a safer interpretation of it. The form supplie - **The first viewport is a thesis, not a header.** Demonstrate the mechanism immediately, at the scale the form has in life; do not trap the concept inside a standard hero or card shell. The memory test: if someone left after one viewport, what would they describe an hour later? If the honest answer is a mood, the concept has not committed yet. - **Prove the hero before building past it.** When an approved comp exists, render the first viewport, capture it, and set it beside the comp's first viewport before any later section: the hero carries the run's ambition, and every following section inherits its shortfall. Judge scale and density as quantities, a field at a tenth of the comp's coverage or type at half its weight is a different design, and a five-minute retry here is what a rebuild verdict at the finish costs when this check is skipped. - **Prove, don't claim.** Show the subject doing its job: the interface at work, the mechanism dramatized, specifics a competitor could not copy-paste. Sections that restate a claim in different words add length, not substance. Demonstration data is design material: author it at full fidelity and label it synthetic; claims stay uninventable. -- **Author the assets; never substitute chrome.** Great surfaces live on carefully made content: names, entries, copy, covers, thumbnails, textures. In greenfield work every blank the ask round left open is yours to author at production fidelity; content is authorable, claims are labelable, no section is omittable. An unanswered commercial claim ships as a clearly marked placeholder on the user's replacement list. When image generation exists, producing the design's imagery is part of building, at the scale the composition needs: a viewport that wants atmosphere gets a full-bleed layered scene, and a library of small centered subjects standardized for tidiness forecloses it. Gradients, glass, and generic icon tiles where an authored asset belongs are the gap wearing chrome; icons drawn in the world's own grammar are the remedy, not the target. +- **Author the assets; never substitute chrome.** Great surfaces live on carefully made content: names, entries, copy, covers, thumbnails, textures. In greenfield work every blank the ask round left open is yours to author at production fidelity; content is authorable, claims are labelable, no section is omittable. An unanswered commercial claim ships as a clearly marked placeholder on the user's replacement list. When image generation exists, producing the design's imagery is part of building, at the scale the composition needs: a viewport that wants atmosphere gets a full-bleed layered scene, and a library of small centered subjects standardized for tidiness forecloses it. Gradients, glass, and generic icon tiles where an authored asset belongs are the gap wearing chrome; icons drawn in the world's own grammar are the remedy, not the target. Generation prompts ban fabrications, never media: "no photography" on a photographic subject deletes the subject, not the risk. - **Build the form's web leverage.** When the chosen world names a technique (canvas, WebGL, view transitions, generative motion), build the technique itself, not a static imitation of it; the graceful fallback serves constrained clients, it is not the default experience. - **Pace the scroll like a studio.** Vary density, scale, image, motion, and quiet inside one grammar; a dense passage earns a quiet one, and the page ends anchored by a real close. One spacing rhythm throughout, with more space above a heading than below it. - **Use real, verified imagery when the brief implies it.** Search for the subject's physical object rather than the category; one decisive photo beats five mediocre ones. Verify stock URLs resolve. diff --git a/skill/reference/visualize.md b/skill/reference/visualize.md index a1c023ce6..32d54a7d4 100644 --- a/skill/reference/visualize.md +++ b/skill/reference/visualize.md @@ -9,7 +9,7 @@ The purpose of a probe is to test composition, narrative, hierarchy, density, fo Render three distinct high-fidelity north-star comps of the requested surface, with whatever generation capability exists, saved under `.impeccable/mocks/` so they survive the session. Comp at the surface's own viewport: portrait at device size for a native app or mobile-first surface, desktop landscape otherwise; a phone screen comped landscape misstates the composition before anything gets built against it. Comps are the build thread's own work, never delegated: the thread that writes the comp prompts holds the direction's full context, and it has already seen every comp when the build starts. Open every image you produce or reference by its workspace-relative path, never an absolute one: sandboxed viewers reject absolute paths, and everything under the project root has a relative path. Base them on the real content and the surface concepts already developed with the user. Three is the number: one comp invites rubber-stamping, and the spread between three is what surfaces the composition worth building. The chosen card's decision comp is the first of the three: it already renders this direction at full fidelity under this file's discipline, so this round generates two more that vary what the first held fixed, and all three go to the approval point together. Only a round that arrives with no decision comp, a degraded roll, an identity-mode page, a direction pinned without the decision round, renders all three here. - A comp is a designed surface, not a picture of the subject. Lead the generation prompt with the surface's own structure, whatever regions this design actually has, named in order with their scale relationships; a page with no navigation states that instead of inventing one, and an unconventional surface states its unconventional skeleton. A prompt that leads with the world's atmosphere gets a vignette back: the model paints the fish market instead of the fish market's website. Self-check every render: if it could hang as a poster, or reads as a photograph or scene with some text on it, it is not a comp; regenerate with the layout scaffold stated more literally. -- The inverse failure fails too: a surface with none of its subject in it. The subject appears as the content the regions exist to hold: a forum about machines shows the machines, a menu shows the dishes, a player shows the work it plays, a dashboard shows the live data it watches. The world dresses the frame and never displaces what the frame exists to show. Self-check every render: point at the subject; a render that depicts everything about the world and nothing of the subject fails however faithful its atmosphere, so regenerate with the subject's imagery named region by region in the prompt. +- The inverse failure fails too: a surface with none of its subject in it. The subject appears as the content the regions exist to hold: a forum about machines shows the machines, a menu shows the dishes, a player shows the work it plays, a dashboard shows the live data it watches. The world dresses the frame and never displaces what the frame exists to show. The deletion usually arrives through the prompt's own exclusion list: ban fabrications, invented counts, testimonials, prices, badges, never a medium. "No photography" on a photographic subject is the timidity reflex wearing honesty's clothes; truth binds claims, not demonstrations, and a photo of the subject doing its job is a demonstration. Self-check every render: point at the subject; a render that depicts everything about the world and nothing of the subject fails however faithful its atmosphere, so regenerate with the subject's imagery named region by region in the prompt. - A comp is judged as the shipped screen, in the surface's mode and on its platform. The visitor's job must be readable from the image alone: an Operate surface shows the task's real controls and data mid-use, a Read surface shows its text at reading scale, a Persuade surface shows what is offered and the action that accepts it, an Experience surface gives the work itself the room. Self-check every render: name the mode from the image with no caption; a render whose mode cannot be read back is art direction without a surface, so regenerate with the visitor's job restated as the prompt's spine. - Commitment is depth, not coverage. The world enters through one dominant move plus the material, type, and spacing that support it, and the remaining regions hold still so that move can be read; a region that simply does its job in the world's own grammar carries the direction further than a region performing the concept. Density is a mode decision, not a fidelity setting: calm is what Operate and Read surfaces are for, and even a Persuade or Experience surface spends its boldness on a focal moment rather than everywhere at once. Self-check every render: count what competes with the focal moment for the eye in the first viewport; a second contender means the comp is shouting, so regenerate keeping the strongest move and quieting the rest. Busy is louder, not bolder. - When the user shortlisted multiple concepts, spread the three across them. From e91c273361ed50e8ab6b403f90c32b6a223e45ae Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Tue, 11 Aug 2026 17:25:04 -0400 Subject: [PATCH 4/6] The density check cuts competition, never content Proven necessary by its own demo: the first re-render of the declined moto-forum comp satisfied subject-present and one-dominant-move by deleting the value proposition, leaving a members' index that told a first-time visitor nothing about what this is or why to care. Paul caught it. Quieting a region means it stops performing, not that it leaves; empty is quieter, not calmer. AI-assisted (Claude Fable 5), prepared for maintainer review. Co-Authored-By: Claude Fable 5 --- skill/reference/visualize.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skill/reference/visualize.md b/skill/reference/visualize.md index 32d54a7d4..ccfd4bc14 100644 --- a/skill/reference/visualize.md +++ b/skill/reference/visualize.md @@ -11,7 +11,7 @@ Render three distinct high-fidelity north-star comps of the requested surface, w - A comp is a designed surface, not a picture of the subject. Lead the generation prompt with the surface's own structure, whatever regions this design actually has, named in order with their scale relationships; a page with no navigation states that instead of inventing one, and an unconventional surface states its unconventional skeleton. A prompt that leads with the world's atmosphere gets a vignette back: the model paints the fish market instead of the fish market's website. Self-check every render: if it could hang as a poster, or reads as a photograph or scene with some text on it, it is not a comp; regenerate with the layout scaffold stated more literally. - The inverse failure fails too: a surface with none of its subject in it. The subject appears as the content the regions exist to hold: a forum about machines shows the machines, a menu shows the dishes, a player shows the work it plays, a dashboard shows the live data it watches. The world dresses the frame and never displaces what the frame exists to show. The deletion usually arrives through the prompt's own exclusion list: ban fabrications, invented counts, testimonials, prices, badges, never a medium. "No photography" on a photographic subject is the timidity reflex wearing honesty's clothes; truth binds claims, not demonstrations, and a photo of the subject doing its job is a demonstration. Self-check every render: point at the subject; a render that depicts everything about the world and nothing of the subject fails however faithful its atmosphere, so regenerate with the subject's imagery named region by region in the prompt. - A comp is judged as the shipped screen, in the surface's mode and on its platform. The visitor's job must be readable from the image alone: an Operate surface shows the task's real controls and data mid-use, a Read surface shows its text at reading scale, a Persuade surface shows what is offered and the action that accepts it, an Experience surface gives the work itself the room. Self-check every render: name the mode from the image with no caption; a render whose mode cannot be read back is art direction without a surface, so regenerate with the visitor's job restated as the prompt's spine. -- Commitment is depth, not coverage. The world enters through one dominant move plus the material, type, and spacing that support it, and the remaining regions hold still so that move can be read; a region that simply does its job in the world's own grammar carries the direction further than a region performing the concept. Density is a mode decision, not a fidelity setting: calm is what Operate and Read surfaces are for, and even a Persuade or Experience surface spends its boldness on a focal moment rather than everywhere at once. Self-check every render: count what competes with the focal moment for the eye in the first viewport; a second contender means the comp is shouting, so regenerate keeping the strongest move and quieting the rest. Busy is louder, not bolder. +- Commitment is depth, not coverage. The world enters through one dominant move plus the material, type, and spacing that support it, and the remaining regions hold still so that move can be read; a region that simply does its job in the world's own grammar carries the direction further than a region performing the concept. Density is a mode decision, not a fidelity setting: calm is what Operate and Read surfaces are for, and even a Persuade or Experience surface spends its boldness on a focal moment rather than everywhere at once. The check cuts competition, never content: quieting a region means it stops performing, not that it leaves, and a first-time visitor still learns what this is, why it matters, and what to do, from the quiet regions doing their jobs. Self-check every render: count what competes with the focal moment for the eye in the first viewport; a second contender means the comp is shouting, so regenerate keeping the strongest move and quieting the rest, and if the answer to "why should I care" left the frame, the cut went through content, so put it back quietly. Busy is louder, not bolder; empty is quieter, not calmer. - When the user shortlisted multiple concepts, spread the three across them. - When one direction is committed, vary the structural uncertainty an image can resolve: topology, sequence, density, hierarchy, focal composition, or interaction framing. - Show enough beyond the opening moment to prove the concept can govern the whole requested surface. From cb305fdca10f0201c5d3fb92b1efc453bbce8bdd Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Tue, 11 Aug 2026 17:53:10 -0400 Subject: [PATCH 5/6] Two adversarial reviews later, the discipline says half as much Two independent skeptic passes over the added prose, one hunting oversteer and example bias, one hunting mode and platform damage. What they killed, and why: - The absolute 'never a medium' rule contradicted the file's own imagery-stance fixity two paragraphs up and stripped legitimate guards (an illustration-committed world, a native app screen warding off stock-photo drift). A medium ban now belongs to the committed imagery stance, never to caution, and the rule appears once per reader context instead of five times corpus-wide. - The quoted incident string and the four-example subject list taught the model the exact framings they existed to prevent. Gone; the abstract rule plus the point-at-the-subject check carry it. - 'A first-time visitor learns what this is, why it matters, and what to do' was Persuade anatomy imposed on all four modes. The guard is now mode-neutral: a quieted region keeps its information and stops performing. - 'Calm is what Operate and Read surfaces are for' contradicted operate.md's density affordance. Deleted; modes stay defined in one place. - The focal-moment count now presupposes nothing: it fires only where the direction names a focal moment, and only on same-scale rivalry, so an even, calm field stops reading as a failure. - The decision-comp clause and the mode bullet no longer restate what they can reference. Net: the prose additions drop from roughly 480 words to under 200, with no quoted strings and no example lists. AI-assisted (Claude Fable 5), prepared for maintainer review. Co-Authored-By: Claude Fable 5 --- skill/agents/impeccable-asset-producer.md | 2 +- skill/reference/new-work.md | 4 ++-- skill/reference/visualize.md | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/skill/agents/impeccable-asset-producer.md b/skill/agents/impeccable-asset-producer.md index 76aa5efc1..59b84a477 100644 --- a/skill/agents/impeccable-asset-producer.md +++ b/skill/agents/impeccable-asset-producer.md @@ -24,7 +24,7 @@ Do not redesign. Preserve the reference's visual role, silhouette, palette, ligh ## Decision Comps -When the parent hands you a decision card packet instead of an approved mock, the job is one comp: one card, one file, written to the card's declared `comp` path the moment it renders. The parent runs several of you in parallel, one per card, so your entire contract is this card; generate first, plan never, because the file on disk is the deliverable and the decision page is waiting on it. Work from the card's structured fields and PRODUCT.md alone; a card too thin to brief a comp is reported back, not padded from imagination. Render the card's direction as a north-star comp at full fidelity: the requested surface's first viewport, prompt led by the surface's own structure (its regions named in order with their scale relationships, never the world's atmosphere), fully committed in the card's own palette, type character, and material world; a native app or mobile-first surface is a portrait frame at its device viewport, never a landscape default. Every sibling renders at the same full fidelity in its own grammar, one surface, one aspect; equal commitment is what keeps the comparison honest. Real product name and real content only; never invent commercial claims, prices, benchmarks, or dates PRODUCT.md does not carry. Your prompt's exclusion list bans fabrications, never media: a subject that lives in photographs keeps its photographs, and writing "no photography" for a photographic subject deletes the subject while the invented-claim risk stays untouched. Write the prompt sidecar beside the file. Return one line naming the path and any deviation, nothing more. Everything below this section is the asset-production job; none of it applies to a decision-comp run. +When the parent hands you a decision card packet instead of an approved mock, the job is one comp: one card, one file, written to the card's declared `comp` path the moment it renders. The parent runs several of you in parallel, one per card, so your entire contract is this card; generate first, plan never, because the file on disk is the deliverable and the decision page is waiting on it. Work from the card's structured fields and PRODUCT.md alone; a card too thin to brief a comp is reported back, not padded from imagination. Render the card's direction as a north-star comp at full fidelity: the requested surface's first viewport, prompt led by the surface's own structure (its regions named in order with their scale relationships, never the world's atmosphere), fully committed in the card's own palette, type character, and material world; a native app or mobile-first surface is a portrait frame at its device viewport, never a landscape default. Every sibling renders at the same full fidelity in its own grammar, one surface, one aspect; equal commitment is what keeps the comparison honest. Real product name and real content only; never invent commercial claims, prices, benchmarks, or dates PRODUCT.md does not carry. Exclusions bind those claims, never a medium the card's own world has not excluded: a subject that lives in photographs keeps its photographs. Write the prompt sidecar beside the file. Return one line naming the path and any deviation, nothing more. Everything below this section is the asset-production job; none of it applies to a decision-comp run. ## Input Contract diff --git a/skill/reference/new-work.md b/skill/reference/new-work.md index 1bb3abaa2..8a422e401 100644 --- a/skill/reference/new-work.md +++ b/skill/reference/new-work.md @@ -48,7 +48,7 @@ The script assigns which structure gets built; your top-ranked structure is what The standing exit: every direction round offers one quiet, permanent alternative, the category standard, played straight. It is the user's door, never yours: never recommend it, never weigh it against the roll, never let it soften the dealt directions; the counterweights bind the unchosen default, not the chosen one. When the user takes it, in the canon action, a safer-steer, or plain words asking for the familiar or competitor-like path, convention becomes the commitment: ask once for two or three products this should sit alongside, make their craft level the bar, and execute the canon at full fidelity, without irony or smuggled quirk. A standing preference gets recorded as a brand commitment in PRODUCT.md. Re-roll eliminates every direction already shown, grounded and challenger alike; after two consecutive re-rolls, ask what quality is missing. You may re-roll on your own only on named factual grounds, when the assigned direction cannot carry the product's truth or task; taste is never grounds. The user may re-roll freely, and a user- or brief-pinned direction beats the roll, always. Present the decision visually: write an options payload with the assigned direction leading and its raised lines included, the pick card when one exists, the dealt challengers as alternates carrying their QUALITY BAR cards plus each challenger's verdict and kept line, re-roll with its safer and bolder registers, steer, plus canon enabled, and `followup: true` when the execution-contract round will follow (it does whenever image generation exists and no standing build-path preference is recorded); a degraded roll with no challengers still uses the page, as a single text-only card with re-roll. Give every card the same anatomy, thesis, palette, materials, first viewport, honest risk, and the challengers' case lines (run the script with `--schema` for the exact shape); the page renders identity from these fields, routes declined challengers to a demoted row on its own, and a challenger's catalog image rides as labeled inspiration, never as the promise of the build. Author `canonCard` too: the category standard as one honest card with the same anatomy; the page keeps it subordinate, and the counterweights still bind you. Run `node {{scripts_path}}/serve-question.mjs --start --payload ` (run it with `--schema` first for the exact payload shape). It daemonizes, prints the page URL and a key, and exits immediately; now open that URL for the user, in-app browser first, then the system opener, then showing the URL. Collect the choice with `--wait --key `, repeating while it exits 3; the ANSWER prints as JSON. Exit 4 means the page was closed without an answer: re-present once through the structured question tool, and with no answer there either, proceed unattended with the assigned direction and state the assumptions. A harness that can leave a shell blocked in the background may instead run the script without `--start` and let it auto-open and block. Only a session where no browser can open at all, headless, CI, an eval worker, a remote shell with no display, puts the same decision through the structured question tool instead; the script self-detects these environments and exits 2 with that advice, so treat exit 2 as this fallback, never as an error to retry. -When image generation exists, every card also declares a `comp` path under `.impeccable/mocks/decision/`, the canon card included. Where the harness sandboxes its shell, start the page through the least-sandboxed command path it offers: a sandboxed shell cannot bind the board's port, and the first-attempt failure costs a retry every session. Serve the page first, then produce the comps; the page shimmer-waits per slot and the user may answer before they land. Each card's image is that direction's north-star comp at full fidelity, produced under the comp discipline in [visualize.md](visualize.md): the requested surface's first viewport, structure-led prompt, real product name and real content, no invented commercial claims, in that card's own palette, type character, and material world, committed all the way, where commitment is depth, not coverage: visualize.md's self-checks, the mode readable from the image, one dominant move, the subject present as content, bind decision comps identically. Generation takes the same time at any fidelity, so an unfinished draft pays draft quality for comp cost; fairness between cards comes from equal fidelity in each card's own grammar, one surface, one aspect, never from shared unfinishedness. The frame's aspect is the surface's own: a native app or mobile-first surface comps portrait at its device viewport, a desktop web surface landscape, and the decision page adapts to either, so a phone screen comped landscape is a broken frame, not a neutral default. Produce in the order the user reads, the assigned card, then the pick, then the full-card hand, then canon, each file written with its prompt sidecar the moment it is done, so a re-roll's spend front-loads onto the cards read first; declined challengers get no comp, their catalog thumb is their face. When the harness runs subagents in parallel, fan the set out as one agent per card: each spawn is the shipped asset producer with a single-comp packet, that card's fields, PRODUCT.md, the shared frame, and the card's declared path, up to four in flight at once. A slot still empty when its agent returns is regenerated inline, and a slot still empty when the user answers is dropped without ceremony; no other supervision is owed. Without parallel subagents, generate in the main thread after serving, in the same reading order, and let the harness's own generation display carry the progress; the wait for the answer follows the last file. The chosen card's comp is not spent by the choice: on a comp-led build it enters the comp round as compositional option one, and on a code-led build it returns at the finish review as the critique reference, what the image dared that the build did not. The unchosen comps stay in `.impeccable/mocks/decision/` as the round's spent hand; they carry no approval and imply none. With no image generation, the cards carry their identity in palette chips and facts, and that page is complete, not a lesser version; the page then also demotes every challenger's catalog art to a labeled thumbnail on its own, because salience must encode the verdict, never the accident of which cards have images. +When image generation exists, every card also declares a `comp` path under `.impeccable/mocks/decision/`, the canon card included. Where the harness sandboxes its shell, start the page through the least-sandboxed command path it offers: a sandboxed shell cannot bind the board's port, and the first-attempt failure costs a retry every session. Serve the page first, then produce the comps; the page shimmer-waits per slot and the user may answer before they land. Each card's image is that direction's north-star comp at full fidelity, produced under the comp discipline in [visualize.md](visualize.md): the requested surface's first viewport, structure-led prompt, real product name and real content, no invented commercial claims, in that card's own palette, type character, and material world, committed all the way; visualize.md's self-checks bind decision comps identically. Generation takes the same time at any fidelity, so an unfinished draft pays draft quality for comp cost; fairness between cards comes from equal fidelity in each card's own grammar, one surface, one aspect, never from shared unfinishedness. The frame's aspect is the surface's own: a native app or mobile-first surface comps portrait at its device viewport, a desktop web surface landscape, and the decision page adapts to either, so a phone screen comped landscape is a broken frame, not a neutral default. Produce in the order the user reads, the assigned card, then the pick, then the full-card hand, then canon, each file written with its prompt sidecar the moment it is done, so a re-roll's spend front-loads onto the cards read first; declined challengers get no comp, their catalog thumb is their face. When the harness runs subagents in parallel, fan the set out as one agent per card: each spawn is the shipped asset producer with a single-comp packet, that card's fields, PRODUCT.md, the shared frame, and the card's declared path, up to four in flight at once. A slot still empty when its agent returns is regenerated inline, and a slot still empty when the user answers is dropped without ceremony; no other supervision is owed. Without parallel subagents, generate in the main thread after serving, in the same reading order, and let the harness's own generation display carry the progress; the wait for the answer follows the last file. The chosen card's comp is not spent by the choice: on a comp-led build it enters the comp round as compositional option one, and on a code-led build it returns at the finish review as the critique reference, what the image dared that the build did not. The unchosen comps stay in `.impeccable/mocks/decision/` as the round's spent hand; they carry no approval and imply none. With no image generation, the cards carry their identity in palette chips and facts, and that page is complete, not a lesser version; the page then also demotes every challenger's catalog art to a labeled thumbnail on its own, because salience must encode the verdict, never the accident of which cards have images. The moment the direction lands, one more round on the same open table decides the execution contract. The direction payload declares `followup: true`, so the table stays open after the pick; deliver the build-path payload through `--update` immediately. Two text-only cards. **Comp-led**: a first-viewport comp is generated and it is law, the finish review audits the build against it; boldest composition on the table, fix rounds expected, motion at risk; choosing it makes the comp non-optional, no silent skipping. **Code-led**: no comp of this page and no apology for it; the QUALITY BAR boards still calibrate finish, and the ambition moves into the written contract, the FIRST VIEWPORT block plus a named signature interaction and motion grammar, which the finish reviewer audits in behavior; code-led is not a discount on commitment, the direction still lands fully committed in code. Lead with the chosen world's fit: a costume-heavy catalog world leads comp-led, a quiet or conventional direction leads code-led; the lead is a default, never a decision, and the user flips it freely. A standing preference, voiced once, is recorded as a brand commitment in PRODUCT.md and skips this round on later surfaces. Without image generation there is no fork and no round: code-led is the only path, stated in one line rather than asked. Only a detached table (`--start`) stays open for `--update`: a blocking serve or the structured-tool channel runs the build-path round as its own second question instead, and `followup: true` belongs only on a detached round. @@ -97,7 +97,7 @@ Build the assigned direction, not a safer interpretation of it. The form supplie - **The first viewport is a thesis, not a header.** Demonstrate the mechanism immediately, at the scale the form has in life; do not trap the concept inside a standard hero or card shell. The memory test: if someone left after one viewport, what would they describe an hour later? If the honest answer is a mood, the concept has not committed yet. - **Prove the hero before building past it.** When an approved comp exists, render the first viewport, capture it, and set it beside the comp's first viewport before any later section: the hero carries the run's ambition, and every following section inherits its shortfall. Judge scale and density as quantities, a field at a tenth of the comp's coverage or type at half its weight is a different design, and a five-minute retry here is what a rebuild verdict at the finish costs when this check is skipped. - **Prove, don't claim.** Show the subject doing its job: the interface at work, the mechanism dramatized, specifics a competitor could not copy-paste. Sections that restate a claim in different words add length, not substance. Demonstration data is design material: author it at full fidelity and label it synthetic; claims stay uninventable. -- **Author the assets; never substitute chrome.** Great surfaces live on carefully made content: names, entries, copy, covers, thumbnails, textures. In greenfield work every blank the ask round left open is yours to author at production fidelity; content is authorable, claims are labelable, no section is omittable. An unanswered commercial claim ships as a clearly marked placeholder on the user's replacement list. When image generation exists, producing the design's imagery is part of building, at the scale the composition needs: a viewport that wants atmosphere gets a full-bleed layered scene, and a library of small centered subjects standardized for tidiness forecloses it. Gradients, glass, and generic icon tiles where an authored asset belongs are the gap wearing chrome; icons drawn in the world's own grammar are the remedy, not the target. Generation prompts ban fabrications, never media: "no photography" on a photographic subject deletes the subject, not the risk. +- **Author the assets; never substitute chrome.** Great surfaces live on carefully made content: names, entries, copy, covers, thumbnails, textures. In greenfield work every blank the ask round left open is yours to author at production fidelity; content is authorable, claims are labelable, no section is omittable. An unanswered commercial claim ships as a clearly marked placeholder on the user's replacement list. When image generation exists, producing the design's imagery is part of building, at the scale the composition needs: a viewport that wants atmosphere gets a full-bleed layered scene, and a library of small centered subjects standardized for tidiness forecloses it. Gradients, glass, and generic icon tiles where an authored asset belongs are the gap wearing chrome; icons drawn in the world's own grammar are the remedy, not the target. - **Build the form's web leverage.** When the chosen world names a technique (canvas, WebGL, view transitions, generative motion), build the technique itself, not a static imitation of it; the graceful fallback serves constrained clients, it is not the default experience. - **Pace the scroll like a studio.** Vary density, scale, image, motion, and quiet inside one grammar; a dense passage earns a quiet one, and the page ends anchored by a real close. One spacing rhythm throughout, with more space above a heading than below it. - **Use real, verified imagery when the brief implies it.** Search for the subject's physical object rather than the category; one decisive photo beats five mediocre ones. Verify stock URLs resolve. diff --git a/skill/reference/visualize.md b/skill/reference/visualize.md index ccfd4bc14..a765d55ed 100644 --- a/skill/reference/visualize.md +++ b/skill/reference/visualize.md @@ -9,9 +9,9 @@ The purpose of a probe is to test composition, narrative, hierarchy, density, fo Render three distinct high-fidelity north-star comps of the requested surface, with whatever generation capability exists, saved under `.impeccable/mocks/` so they survive the session. Comp at the surface's own viewport: portrait at device size for a native app or mobile-first surface, desktop landscape otherwise; a phone screen comped landscape misstates the composition before anything gets built against it. Comps are the build thread's own work, never delegated: the thread that writes the comp prompts holds the direction's full context, and it has already seen every comp when the build starts. Open every image you produce or reference by its workspace-relative path, never an absolute one: sandboxed viewers reject absolute paths, and everything under the project root has a relative path. Base them on the real content and the surface concepts already developed with the user. Three is the number: one comp invites rubber-stamping, and the spread between three is what surfaces the composition worth building. The chosen card's decision comp is the first of the three: it already renders this direction at full fidelity under this file's discipline, so this round generates two more that vary what the first held fixed, and all three go to the approval point together. Only a round that arrives with no decision comp, a degraded roll, an identity-mode page, a direction pinned without the decision round, renders all three here. - A comp is a designed surface, not a picture of the subject. Lead the generation prompt with the surface's own structure, whatever regions this design actually has, named in order with their scale relationships; a page with no navigation states that instead of inventing one, and an unconventional surface states its unconventional skeleton. A prompt that leads with the world's atmosphere gets a vignette back: the model paints the fish market instead of the fish market's website. Self-check every render: if it could hang as a poster, or reads as a photograph or scene with some text on it, it is not a comp; regenerate with the layout scaffold stated more literally. -- The inverse failure fails too: a surface with none of its subject in it. The subject appears as the content the regions exist to hold: a forum about machines shows the machines, a menu shows the dishes, a player shows the work it plays, a dashboard shows the live data it watches. The world dresses the frame and never displaces what the frame exists to show. The deletion usually arrives through the prompt's own exclusion list: ban fabrications, invented counts, testimonials, prices, badges, never a medium. "No photography" on a photographic subject is the timidity reflex wearing honesty's clothes; truth binds claims, not demonstrations, and a photo of the subject doing its job is a demonstration. Self-check every render: point at the subject; a render that depicts everything about the world and nothing of the subject fails however faithful its atmosphere, so regenerate with the subject's imagery named region by region in the prompt. -- A comp is judged as the shipped screen, in the surface's mode and on its platform. The visitor's job must be readable from the image alone: an Operate surface shows the task's real controls and data mid-use, a Read surface shows its text at reading scale, a Persuade surface shows what is offered and the action that accepts it, an Experience surface gives the work itself the room. Self-check every render: name the mode from the image with no caption; a render whose mode cannot be read back is art direction without a surface, so regenerate with the visitor's job restated as the prompt's spine. -- Commitment is depth, not coverage. The world enters through one dominant move plus the material, type, and spacing that support it, and the remaining regions hold still so that move can be read; a region that simply does its job in the world's own grammar carries the direction further than a region performing the concept. Density is a mode decision, not a fidelity setting: calm is what Operate and Read surfaces are for, and even a Persuade or Experience surface spends its boldness on a focal moment rather than everywhere at once. The check cuts competition, never content: quieting a region means it stops performing, not that it leaves, and a first-time visitor still learns what this is, why it matters, and what to do, from the quiet regions doing their jobs. Self-check every render: count what competes with the focal moment for the eye in the first viewport; a second contender means the comp is shouting, so regenerate keeping the strongest move and quieting the rest, and if the answer to "why should I care" left the frame, the cut went through content, so put it back quietly. Busy is louder, not bolder; empty is quieter, not calmer. +- The inverse failure fails too: a surface with none of its subject in it. The subject appears as the content the regions exist to hold; the world dresses the frame and never displaces what the frame exists to show. The deletion usually rides in on the prompt's exclusion list, so exclusions bind invented claims, and a medium ban belongs to the committed imagery stance, never to caution. Before accepting a render, point at the subject: a render that depicts everything about the world and nothing of the subject fails however faithful its atmosphere, so regenerate with the subject's content named region by region. +- A comp is judged as the shipped screen: the visitor's job must be readable from the image alone. Name the surface's mode from the render with no caption; a render whose mode cannot be read back is art direction without a surface, so regenerate with the visitor's job as the prompt's spine. +- Commitment is depth, not coverage. The world enters through one dominant move plus the material, type, and spacing that support it, and the remaining regions hold still so that move can be read; a region that simply does its job in the world's own grammar carries the direction further than a region performing the concept. The check cuts competition, never content: a quieted region keeps its information and stops performing. Where the direction names a focal moment, a second element competing with it at the same scale means the comp is shouting; regenerate keeping the strongest move and quieting the rest. Busy is louder, not bolder. - When the user shortlisted multiple concepts, spread the three across them. - When one direction is committed, vary the structural uncertainty an image can resolve: topology, sequence, density, hierarchy, focal composition, or interaction framing. - Show enough beyond the opening moment to prove the concept can govern the whole requested surface. From 53a69476538bc1b66479f6586ed755d1da66b1e7 Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Wed, 12 Aug 2026 14:29:59 -0400 Subject: [PATCH 6/6] Close the unnamed-focal-moment gap; unstutter the inverse-failure lead Cursor's finding was real: gating the density check on a named focal moment let a busy comp pass whenever the direction named none, which is the common case on the lane that produced the busy comps. The second leg reuses the bullet's own distinction: several regions performing the concept at once is the same shout; regions doing their jobs are not. AI-assisted (Claude Fable 5). Co-Authored-By: Claude Fable 5 --- skill/reference/visualize.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skill/reference/visualize.md b/skill/reference/visualize.md index a765d55ed..244ce58c4 100644 --- a/skill/reference/visualize.md +++ b/skill/reference/visualize.md @@ -9,9 +9,9 @@ The purpose of a probe is to test composition, narrative, hierarchy, density, fo Render three distinct high-fidelity north-star comps of the requested surface, with whatever generation capability exists, saved under `.impeccable/mocks/` so they survive the session. Comp at the surface's own viewport: portrait at device size for a native app or mobile-first surface, desktop landscape otherwise; a phone screen comped landscape misstates the composition before anything gets built against it. Comps are the build thread's own work, never delegated: the thread that writes the comp prompts holds the direction's full context, and it has already seen every comp when the build starts. Open every image you produce or reference by its workspace-relative path, never an absolute one: sandboxed viewers reject absolute paths, and everything under the project root has a relative path. Base them on the real content and the surface concepts already developed with the user. Three is the number: one comp invites rubber-stamping, and the spread between three is what surfaces the composition worth building. The chosen card's decision comp is the first of the three: it already renders this direction at full fidelity under this file's discipline, so this round generates two more that vary what the first held fixed, and all three go to the approval point together. Only a round that arrives with no decision comp, a degraded roll, an identity-mode page, a direction pinned without the decision round, renders all three here. - A comp is a designed surface, not a picture of the subject. Lead the generation prompt with the surface's own structure, whatever regions this design actually has, named in order with their scale relationships; a page with no navigation states that instead of inventing one, and an unconventional surface states its unconventional skeleton. A prompt that leads with the world's atmosphere gets a vignette back: the model paints the fish market instead of the fish market's website. Self-check every render: if it could hang as a poster, or reads as a photograph or scene with some text on it, it is not a comp; regenerate with the layout scaffold stated more literally. -- The inverse failure fails too: a surface with none of its subject in it. The subject appears as the content the regions exist to hold; the world dresses the frame and never displaces what the frame exists to show. The deletion usually rides in on the prompt's exclusion list, so exclusions bind invented claims, and a medium ban belongs to the committed imagery stance, never to caution. Before accepting a render, point at the subject: a render that depicts everything about the world and nothing of the subject fails however faithful its atmosphere, so regenerate with the subject's content named region by region. +- The inverse is also a failure: a surface with none of its subject in it. The subject appears as the content the regions exist to hold; the world dresses the frame and never displaces what the frame exists to show. The deletion usually rides in on the prompt's exclusion list, so exclusions bind invented claims, and a medium ban belongs to the committed imagery stance, never to caution. Before accepting a render, point at the subject: a render that depicts everything about the world and nothing of the subject fails however faithful its atmosphere, so regenerate with the subject's content named region by region. - A comp is judged as the shipped screen: the visitor's job must be readable from the image alone. Name the surface's mode from the render with no caption; a render whose mode cannot be read back is art direction without a surface, so regenerate with the visitor's job as the prompt's spine. -- Commitment is depth, not coverage. The world enters through one dominant move plus the material, type, and spacing that support it, and the remaining regions hold still so that move can be read; a region that simply does its job in the world's own grammar carries the direction further than a region performing the concept. The check cuts competition, never content: a quieted region keeps its information and stops performing. Where the direction names a focal moment, a second element competing with it at the same scale means the comp is shouting; regenerate keeping the strongest move and quieting the rest. Busy is louder, not bolder. +- Commitment is depth, not coverage. The world enters through one dominant move plus the material, type, and spacing that support it, and the remaining regions hold still so that move can be read; a region that simply does its job in the world's own grammar carries the direction further than a region performing the concept. The check cuts competition, never content: a quieted region keeps its information and stops performing. Where the direction names a focal moment, a second element competing with it at the same scale means the comp is shouting; where it names none, several regions performing the concept at once is the same shout. Regenerate keeping the strongest move and quieting the rest. Busy is louder, not bolder. - When the user shortlisted multiple concepts, spread the three across them. - When one direction is committed, vary the structural uncertainty an image can resolve: topology, sequence, density, hierarchy, focal composition, or interaction framing. - Show enough beyond the opening moment to prove the concept can govern the whole requested surface.