From e409bec7b54ba215a7f3bf341fa7e2af84a7e4e1 Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Wed, 22 Jul 2026 09:53:49 -0700 Subject: [PATCH] Canon standing exit, chosen-card directive, and the ambition fixes From Paul's approved UX and the eb686f36 session post-mortem: The standing exit: direction rounds carry a quiet, permanent "Play it straight" action (payload flag canon, reserved id) on the decision page and as the last structured-tool option. It is the user's door, never the model's: never recommended, never weighed against the roll, and choosing it swaps the bar rather than lowering it, two or three named reference products become the craft level, canon executed at full commitment. Safer/conventional steers resolve here, never to a stranger re-roll. Session fixes, each mechanical where possible: the ANSWER line now names the chosen card's hero and board and directs opening them before code (the session built from text alone after viewing a different world's card); generation scale joins the imagery rule (a library of centered 128px subjects foreclosed the atmospheric hero); DESIGN.md rules are checked against the world's native devices and never added to silence a hook finding (the session banned arcade lettering's own offset shadow and laundered 8px through the ramp); staging joins the FORM contract block (the axis was dropped silently at world-choice); the finishing reviewer audits the ceiling against the QUALITY BAR card after persistence (floor rigor was disguising unreached ambition); the icon-tile clause names hand-drawn icons as remedy, not target. Dist rebuild deferred: the release-gate campaign reads the pinned dist. Co-Authored-By: Claude Fable 5 --- skill/reference/new-work.md | 16 ++++++++----- skill/scripts/serve-question.mjs | 41 ++++++++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/skill/reference/new-work.md b/skill/reference/new-work.md index 714d15a35..0b67ffd61 100644 --- a/skill/reference/new-work.md +++ b/skill/reference/new-work.md @@ -44,9 +44,11 @@ The script assigns which structure gets built: your top-ranked structure is what 2. From that cultural world, list seven concrete visual systems, artifacts, places, or rituals the audience knows by heart, each with one line on why it resonates and can carry the mechanism, ordered by resonance. The audience's world includes its graphic and screen traditions, the notation, publications, identity programs, data graphics, and interfaces it reads daily, not only its physical objects; a nameable abstract system (a school of poster, a documentation standard, a data-graphic tradition) is as concrete a candidate as any artifact. What would this thing look like as a physical object; what did its world look like before the web? Near-duplicates count once. When more than three of the seven share one material family, the derivation stopped at the subject's most obvious artifact; the audience's world is larger than that, so dig until the list spans at least three families. 3. Turn that material into complete directions: each joins a reusable visual world to a concrete first-surface experience. 4. Run `node {{scripts_path}}/concept-seed.mjs --scope direction --mode ` and follow what it prints. The script assigns which direction gets built and deals catalog challengers. Fuse each challenger before judging it: the challenger supplies the form and its system grammar, the product supplies every fact, and clarity wins conflicts. Weigh fused challengers against the assigned direction on exactly two axes, audience identification and product clarity; losing to strong grounded material is a valid outcome, and beating a thin or tool-monoculture list is the point. -5. Present one direction, fully committed: its world, first viewport, visitor path, signature interaction, cross-surface reach, and honest risk. Alongside it, offer the one or two fused challengers that survived the weighing as named alternates with a one-line case each, plus re-roll with an optional one-line steer. What you never present is a ranked menu of your own grounded candidates; a lineup of those invites the safest card, while dealt challengers carry no such rut. The two channels share this structure and differ only in richness: cards and boards on the decision page, names and one-liners through the structured tool. 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. Pick the channel by capability, not by habit: can you put a page in front of the user, through an in-app browser or by opening a browser window on their machine outside your harness? If yes, present the decision visually: write an options payload with the assigned direction leading, the dealt challengers as alternates carrying their QUALITY BAR cards, and re-roll plus steer enabled; a degraded roll with no challengers still uses the page, as a single text-only card with re-roll, then 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, preferring the in-app browser when the harness has one, 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. If no, because the session is headless, CI, an eval worker, or a remote shell with no display, skip the page and put the same decision through the structured question tool; 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. +5. Present one direction, fully committed: its world, first viewport, visitor path, signature interaction, cross-surface reach, and honest risk. Alongside it, offer the one or two fused challengers that survived the weighing as named alternates with a one-line case each, plus re-roll with an optional one-line steer. What you never present is a ranked menu of your own grounded candidates; a lineup of those invites the safest card, while dealt challengers carry no such rut. The two channels share this structure and differ only in richness: cards and boards on the decision page, names and one-liners through the structured tool; the structured tool's option list also carries the standing exit as its last option. -Catalog worlds are working systems, not mood references. When one survives, carry its palette and material, type and composition, topology, controls and state, and responsive rules into the product. When the source is itself an interface language, commit to its native grammar across navigation, content, controls, and states. When the harness can view images, open the QUALITY BAR board and hero the seed prints for the world you build (when it only reads local images, download the card to a temp file first and view that): they set the craft level the build must reach, the finish, commitment, and art direction of a rendered reference, and never dictate the composition; your surface serves this product. +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 take it on your own judgment, and never let its existence soften the dealt directions; the counterweights bind the unchosen default, not the chosen one. When the user takes it, or asks in a steer or in any words for the safe, 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 quality bar, and execute the category's canon at full fidelity, conventions embraced without irony, without a smuggled quirk, and without the half-heartedness that treats the choice as a defeat. A steer asking for safer or more conventional resolves here, never to a stranger re-roll. If the user states it as a standing preference, record it as a brand commitment in PRODUCT.md so no future surface re-litigates it. 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. Pick the channel by capability, not by habit: can you put a page in front of the user, through an in-app browser or by opening a browser window on their machine outside your harness? If yes, present the decision visually: write an options payload with the assigned direction leading, the dealt challengers as alternates carrying their QUALITY BAR cards, and re-roll, steer, plus canon enabled; a degraded roll with no challengers still uses the page, as a single text-only card with re-roll, then 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, preferring the in-app browser when the harness has one, 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. If no, because the session is headless, CI, an eval worker, or a remote shell with no display, skip the page and put the same decision through the structured question tool; 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. + +Catalog worlds are working systems, not mood references. When one survives, carry its palette and material, type and composition, topology, controls and state, and responsive rules into the product. When the source is itself an interface language, commit to its native grammar across navigation, content, controls, and states. When the harness can view images, open the QUALITY BAR board and hero the seed prints for the world you build, at the moment the choice lands, and again even if you viewed a different world's card earlier in the round; a card opened for calibration before the choice tells you nothing about the world the user actually picked, and the ANSWER line names the chosen card's images for exactly this reason (when the harness only reads local images, download the card to a temp file first and view that). They set the craft level the build must reach, the finish, commitment, and art direction of a rendered reference, and never dictate the composition; your surface serves this product. Every direction the roll can land on must already be viable: every relationship and claim it visualizes true, a real palette and component family, a distinctive composition with one product-specific experience, workable at full-surface scale within the available assets, tools, and performance budget. A candidate that fails on truth is replaced before the roll, never rescued by it. Truth binds claims, not demonstrations: in greenfield work, author whatever illustrative material the concept needs at full fidelity, label it synthetic wherever a visitor could mistake it for the real thing, and hand the user the list of what to replace with real material. What stays uninventable are commercial and factual claims: prices, customers, benchmarks, endpoints, capabilities the product does not have. Refusing a bold direction because its demonstration data does not exist yet is the timidity reflex wearing honesty's clothes. @@ -66,10 +68,12 @@ Your measured rendition prior: warm, bookish, family, and child-facing subjects ## 5. Record the decision -Before code, state the chosen direction as a contract in the artifact's opening comment, five short blocks, 150 words at most. THESIS: the one idea this surface owns and the category-default arrangement it refuses. OWN-WORLD: the palette and component language, specific enough to be recognizable with all content removed. STORY: what the visitor understands, believes, and does. FIRST VIEWPORT: the exact composition, what is where and at what scale, and where the primary action sits. FORM: the chosen form, its position on your ordered list, and the seed key the script printed. If a block reads like a mood, the direction is not decided yet; the finishing review audits the render against this contract. +Before code, state the chosen direction as a contract in the artifact's opening comment, five short blocks, 150 words at most. THESIS: the one idea this surface owns and the category-default arrangement it refuses. OWN-WORLD: the palette and component language, specific enough to be recognizable with all content removed. STORY: what the visitor understands, believes, and does. FIRST VIEWPORT: the exact composition, what is where and at what scale, and where the primary action sits. FORM: the chosen form, its position on your ordered list, the staging chosen for it, and the seed key the script printed; when the seed dealt stagings, choosing the world does not silently discharge the staging decision, so name the one you commit to. If a block reads like a mood, the direction is not decided yet; the finishing review audits the render against this contract. When a new or replacement world is chosen, DESIGN.md is part of recording the decision, not an aftercare step: write it at the appropriate project or app boundary using [document.md](document.md) before the first build edit lands, in the same working stretch as the direction contract. Record only durable system rules; exact tokens may remain provisional until the first build establishes them, and you update the file when the build settles them. A new world shipped with no DESIGN.md is an incomplete run, exactly as a missing PRODUCT.md is; the finishing review checks the file exists and matches the built world. An ordinary extension does not rewrite DESIGN.md. +Two ways a DESIGN.md rule goes wrong, both observed: a rule you invent that bans a device the world itself uses natively (arcade lettering lives on its offset shadow, print on its misregistration), which reads as discipline while quietly fighting the form, so check every prohibition you write against the world's own materials; and a token added to DESIGN.md so a hook finding stops firing, which launders the value instead of judging it, so a value earns its place by the world and by legibility, never by silencing a check. + If the work establishes durable strategy for a route or artifact, read its existing surface brief, then update it: `node {{scripts_path}}/surface-brief.mjs read ` @@ -88,8 +92,8 @@ 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, 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; never invent prices, customers, benchmarks, or capabilities. -- **Author the assets; never substitute chrome.** Great surfaces live on carefully made content: names, entries, titles, 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, and no section is omittable. When a commercial claim stayed unanswered, ship a clearly marked placeholder value and hand the user the replacement list; a section thinned or dropped for missing truth is the asset gap wearing honesty's clothes. Decoration compensating for missing content, gradients, glass, borders, icon tiles where an authored asset belongs, is the same gap wearing chrome's. -- **Generate the imagery the build needs.** When any image generation is available, a harness-native tool or the API fallback context.mjs reports, generating the design's imagery is part of building: heroes, covers, demonstration thumbnails, textures, marks. Gray boxes and stock-styled placeholders are not a fallback while generation is one command away; state the cost once before the first render and batch what the surface needs. +- **Author the assets; never substitute chrome.** Great surfaces live on carefully made content: names, entries, titles, 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, and no section is omittable. When a commercial claim stayed unanswered, ship a clearly marked placeholder value and hand the user the replacement list; a section thinned or dropped for missing truth is the asset gap wearing honesty's clothes. Decoration compensating for missing content, gradients, glass, borders, generic icon tiles where an authored asset belongs, is the same gap wearing chrome's; icons drawn by hand in the world's own grammar are authored assets, the remedy this rule protects, never its target. +- **Generate the imagery the build needs.** When any image generation is available, a harness-native tool or the API fallback context.mjs reports, generating the design's imagery is part of building: heroes, covers, demonstration thumbnails, textures, marks. Gray boxes and stock-styled placeholders are not a fallback while generation is one command away; state the cost once before the first render and batch what the surface needs. Generate at the scale the composition needs: a first viewport that wants atmosphere is generated as a scene, full-bleed and layered, with foreground, subject, and depth; a library of small centered subjects on plain grounds cannot compose into one, and standardizing every asset to icon scale for tidiness or file size forecloses the page's ambition before a line of code exists. - **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. @@ -101,4 +105,4 @@ Preserve semantics, accessibility, performance, responsiveness, project conventi Inspect desktop and mobile, critique the render against the user's request, the direction contract, and DESIGN.md, fix material gaps, and re-inspect. On a Persuade surface, verify the mode did its job: a first-time visitor should know what this is, why it matters, and what to do within seconds, in the form's own vocabulary. After a first implementation of a new world, update DESIGN.md with the exact tokens and behaviors that survived the build. -When the harness can run a separate agent, this review belongs there, not in the build thread: give it the original request, confirmed answers, the artifact path, its direction contract, DESIGN.md, and existing hook findings. The reviewer's first check is persistence: on a new or replacement world, PRODUCT.md and DESIGN.md exist and DESIGN.md matches the built world; a missing file fails the review before any craft point is scored. Then ask for a short list of material fixes, promise by promise against the contract, apply them, and finish. Do not run a second detector. +When the harness can run a separate agent, this review belongs there, not in the build thread: give it the original request, confirmed answers, the artifact path, its direction contract, DESIGN.md, and existing hook findings. The reviewer's first check is persistence: on a new or replacement world, PRODUCT.md and DESIGN.md exist and DESIGN.md matches the built world; a missing file fails the review before any craft point is scored. Its second check is the ceiling, against the chosen world's QUALITY BAR card when one exists: name the world's native devices the build left unused, its frame, its depth, its lettering treatment, its ornament density, because rigor about the floor, contrast, tokens, breakpoints, is very good at disguising a build that never reached for the card's level. Then ask for a short list of material fixes, promise by promise against the contract, apply them, and finish. Do not run a second detector. diff --git a/skill/scripts/serve-question.mjs b/skill/scripts/serve-question.mjs index 05270c5f3..42c6aeaec 100644 --- a/skill/scripts/serve-question.mjs +++ b/skill/scripts/serve-question.mjs @@ -30,6 +30,8 @@ * }, ... * ], * "reroll": true, // adds a re-roll action (returns {"optionId":"reroll"}) + * "canon": true, // adds the quiet "Play it straight" standing exit + * // (returns {"optionId":"canon"}); direction rounds only * "steer": true // adds a free-text steer field returned with any answer * } * @@ -93,6 +95,24 @@ if (wantsBrowser && !process.env.IMPECCABLE_QUESTION_FORCE) { } } +// Both answer channels (blocking stdout and --wait collection) print through +// this: the ANSWER line, then a directive to open the chosen card's imagery +// when it has any. The card viewing happens at the moment of choice, in the +// working turn, because a build that never reopens the chosen world's board +// and hero calibrates on nothing. +function printAnswer(raw) { + console.log(`ANSWER: ${raw}`); + try { + const a = JSON.parse(raw); + if (a.hero || a.board) { + console.log("CHOSEN CARD: open the chosen world's board and hero images now, before any code (download local copies first when your harness only reads files); they set the craft bar the build must reach."); + } + if (a.optionId === 'canon') { + console.log('CANON CHOSEN: the user picked the category standard on purpose. Ask once for two or three products this should sit alongside; their craft level becomes the quality bar. Execute the canon at full commitment, conventions embraced without irony or smuggled quirk.'); + } + } catch { /* raw answer */ } +} + const payloadPath = arg('payload'); const timeoutSec = Number(arg('timeout', '900')); const portArg = Number(arg('port', '0')); @@ -109,9 +129,10 @@ if (hasFlag('schema')) { { id: 'challenger-teletext', label: 'Teletext Service', lineage: 'broadcast teletext magazines', body: 'Fused alternate.', hero: 'https://impeccable.style/worlds/cards/broadcast-programming-teletext-service-hero.webp' }, ], reroll: true, + canon: true, steer: true, }, null, 2)); - console.log('\nOption ids return verbatim in ANSWER; "reroll" is reserved. hero/board accept URLs or local paths.'); + console.log('\nOption ids return verbatim in ANSWER; "reroll" and "canon" are reserved. hero/board accept URLs or local paths. canon adds a quiet standing "Play it straight" action for direction decisions: the user\'s explicit door to the category standard. Include it only for visual-direction rounds; never present canon as your own recommendation.'); process.exit(0); } @@ -144,7 +165,7 @@ if (hasFlag('wait')) { } if (!answered()) { console.log(`WAITING: no answer yet after ${pollSec}s; run --wait --key ${key} again`); process.exit(3); } const collected = fs.readFileSync(answerFile(key), 'utf8').trim(); - console.log(`ANSWER: ${collected}`); + printAnswer(collected); // A re-roll keeps the table open: the server stays alive awaiting --update, // so only the answer file is consumed. Terminal choices clean up fully. let isRerollAnswer = false; @@ -354,6 +375,11 @@ function page() { #reroll { display: inline-flex; align-items: center; align-self: stretch; gap: 8px; padding: 0 16px; font-family: var(--ks-mono); font-size: .72rem; letter-spacing: .08em; text-transform: uppercase; color: var(--ks-kinpaku); background: transparent; border: 1px solid var(--ks-rule); border-radius: 6px; cursor: pointer; transition: border-color .2s ease, color .2s ease; } #reroll:hover { color: var(--ks-kinpaku-pale); border-color: var(--ks-kinpaku-deep); } #reroll svg { width: 15px; height: 15px; } + /* The quiet exit: always available, never argued with, visually subordinate + to the dealt cards and the re-roll so it reads as the user's own door, + not a recommendation. */ + #canon { align-self: center; padding: 0 4px; font-family: var(--ks-mono); font-size: .66rem; letter-spacing: .08em; text-transform: uppercase; color: inherit; opacity: .45; background: transparent; border: none; border-bottom: 1px dotted currentColor; cursor: pointer; transition: opacity .2s ease; } + #canon:hover { opacity: .85; } .card.skeleton .media { background: var(--ks-graphite); } .shimmer { width: 100%; height: 100%; background: linear-gradient(100deg, var(--ks-graphite) 35%, var(--ks-graphite-2) 50%, var(--ks-graphite) 65%); background-size: 220% 100%; animation: shimmer 1.4s linear infinite; } .card.skeleton .line { height: 11px; border-radius: 4px; background: linear-gradient(100deg, var(--ks-graphite) 35%, var(--ks-graphite-2) 50%, var(--ks-graphite) 65%); background-size: 220% 100%; animation: shimmer 1.4s linear infinite; } @@ -388,6 +414,7 @@ function page() {
${payload.steer ? '' : ''} ${payload.reroll ? '' : ''} + ${payload.canon ? '' : ''}