mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-11 21:57:14 +03:00
main
19
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
720628f4ea |
Fix: use system fonts and the light theme in the picker (#751)
Replace external fonts with system stacks and an outlined SVG logo. Adopt the light paper theme, refine Kinpaku accents, and preserve picker interactions with regression coverage. AI assistance: Codex, under maintainer direction. |
||
|
|
e2ff625b63 |
The Rust engine: one binary replaces every script and the JS detector, fully open (#714)
* Add oracle harness: verb goldens and function-level vectors
Records stdout/stderr/exit/files for every impeccable verb over a fixed
corpus and replays them against an alternate implementation. Adds a loader
hook that captures per-function call vectors from the pure engine modules.
Prepared with AI assistance (Claude Code).
* Oracle: hook, hook-before-edit, hook-admin cases and goldens
Prepared with AI assistance (Claude Code).
* Add docs/CLI-CONTRACT.md: observable behavior of every impeccable verb
Prepared with AI assistance (Claude Code).
* Oracle: context/doctor/pin/surface-brief/critique/palette/embed/signals/csp/seed/genimg/question cases and goldens
Prepared with AI assistance (Claude Code).
* Oracle: live-mode cases and goldens (roots, inject, wrap, insert, accept, session, manual edits, daemon)
Prepared with AI assistance (Claude Code).
* Oracle: mask the binary path before HOME; export launcher env to the binary
Prepared with AI assistance (Claude Code).
* detect: set process.exitCode instead of exiting after the final write
process.exit() right after a large piped stdout write truncated JSON output
at the pipe buffer boundary; found by the oracle harness. Re-record the six
directory-scan goldens that had captured the truncation.
Prepared with AI assistance (Claude Code).
* Oracle: normalize the hook-admin command in both runtimes' forms and audit chars
Prepared with AI assistance (Claude Code).
* Skill text: invoke the impeccable launcher instead of node scripts
Every `node {{scripts_path}}/<name>.mjs` becomes `{{scripts_path}}/impeccable <verb>`
(context-signals -> signals, hook-admin -> hooks). Setup step 1 drops Node, points
Windows shells without sh at impeccable.cmd, and says the launcher runs a
self-contained binary. allowed-tools follows.
Prepared with AI assistance (Claude Code).
* Scripts dir: replace the Node scripts with the impeccable launcher
skill/scripts keeps command-metadata.json and the page JS; every .mjs entry
point, lib/, and live/ are gone (the binary owns those verbs). Adds the POSIX
launcher, impeccable.cmd, VERSION (copied from the new root ENGINE_VERSION),
scripts/fetch-engine.mjs (bun run fetch:engine) to pull the pinned binary
into skill/scripts/bin/<os>-<arch>/, and gitignores that bin dir.
Prepared with AI assistance (Claude Code).
* Build: ship the launcher instead of bundling the JS engine
readSourceFiles no longer copies cli/engine into the skill; the scripts
payload is the launcher (executable bit preserved through dist, plugin/, and
universal.zip), impeccable.cmd, VERSION (synced from ENGINE_VERSION on every
build), the page JS, and command-metadata.json. Hook manifests call
`<scripts>/impeccable hook` behind an existence guard (Codex adds a
commandWindows sibling calling impeccable.cmd; Cursor runs hook-before-edit;
GitHub keeps the git rev-parse form; Grok mirrors Claude); the Node probe and
systemMessage notice are gone. build:release fetches the pinned engine for
every target (lenient) and stages bin/<os-arch>/ into the dist skill copies
after root harness dirs and plugin/ were synced, so git-delivered trees stay
launcher-only. The detection-rule count check reads the vendored
extension/detector/antipatterns.json and is skipped when absent.
build:browser is a stub; the codex prefix rewrite leaves
`{{scripts_path}}/impeccable` alone.
Prepared with AI assistance (Claude Code).
* CLI: turn the impeccable npm package into a platform-binary shim
cli/engine, cli/lib, and cli/bin/commands are gone; their behavior lives in
the engine binary. cli/bin/cli.js now resolves the binary from IMPECCABLE_BIN,
the @impeccable/cli-<os>-<arch> optional dependency (templates under
cli/platform-packages/, published by the engine release), the
~/.impeccable/bin/<version>/ cache, or a checksum-verified download, and
execs it. package.json drops the engine dependencies and the library
exports; puppeteer moves to devDependencies for the icon scripts.
README.npm.md describes the shim.
Prepared with AI assistance (Claude Code).
* Tests: gate behavior on the oracle and the engine binary
Unit tests of the deleted Node scripts and the JS detector are removed;
their behavior is pinned by tests/oracle goldens (frozen JS behavior plus
reviewed deltas) and the engine's own tests. tests/oracle.test.mjs replays
the corpus against the binary (IMPECCABLE_BIN or skill/scripts/bin/<target>/,
via tests/lib/engine-bin.mjs) and skips cleanly without one; the framework
fixture sweep drives live-inject, live-wrap, and detect-csp through the
binary the same way. record.mjs learns --bin. The function-level vectors
under tests/oracle/vectors/calls are committed as the frozen snapshot they
can no longer be regenerated from. Suites: core trimmed to build and
transformer tests, oracle added to the default run, detector/live reduced to
packaging and reference checks, the live-e2e helper tests move to the opt-in
live-e2e lane pending its retarget, cli-remote-e2e is an empty placeholder.
Prepared with AI assistance (Claude Code).
* Docs: describe the launcher, the engine pin, and the oracle gate
CLAUDE.md gains an Engine binary section (launcher lookup order, ENGINE_VERSION,
untracked binaries, how tests get one, the oracle as behavior gate, what stays
JavaScript) and drops the Node-script and JS-detector descriptions; the CLI
and detection-rule sections point at the shim and the engine repo. README.md
states the skill needs no runtime and lists the launcher-based hook commands;
AGENTS.md follows. CLI-CONTRACT.md's intro notes the scripts it quotes are
the recorded source, not the tree.
Prepared with AI assistance (Claude Code).
* Tests: tighten the hook command guard assertion
Prepared with AI assistance (Claude Code).
* Oracle: re-golden 46 cases for the engine's own command names; record them in DELTAS.md
Prepared with AI assistance (Claude Code).
* Build: ship launcher-only release zips by default
IMPECCABLE_BUNDLE_ENGINE=1 opts in to staging the engine binaries into the
dist skill copies. Bundling every target into every provider copy put
dist/universal.zip near 340 MB, past the 25 MB Cloudflare Pages file cap
that impeccable install downloads through.
Prepared with AI assistance (Claude Code).
* Tests: drive the live-e2e orchestrator through the engine binary
The session, fake-agent loop, steer test, and manual-edit probe spawn
<binary> <verb> (live-server, live, live-inject, live-wrap, live-insert,
live-accept, live-poll, live-complete) resolved by tests/lib/engine-bin.mjs
instead of node skill/scripts/live-*.mjs; the completion typing the agent
imported from the deleted live/completion.mjs is a small local helper. The
live-e2e helper unit tests move back into the default live suite (the steer
loop skips without a binary).
Prepared with AI assistance (Claude Code).
* Tests: run new-work-e2e through the engine's serve-question and generate-image verbs
Prepared with AI assistance (Claude Code).
* Tests: point the skill-behavior harness at the launcher and engine binary
The bash tool exports IMPECCABLE_BIN so the staged skill's launcher runs
without a download; scenarios assert on 'impeccable context' instead of
context.mjs and skip without a binary.
Prepared with AI assistance (Claude Code).
* Tests: note what plugin-e2e validates before and after the generated-output sync
Prepared with AI assistance (Claude Code).
* Oracle: record the engine's 'wasm-unsafe-eval' CSP meta patch as a reviewed delta
Prepared with AI assistance (Claude Code).
* Rebase reconciliation: fold main's post-freeze work into the swapped tree
The rebase onto origin/main brought changes whose JS engine halves left the
tree with the swap. This commit reconciles what survives:
- Suite map: register main's comp-fidelity unit tests (build-phase,
comp-diff, font-match, hero-checks) in the core suite and
live-browser-ignores in the live suite.
- Payload guard: the skill scripts payload now allowlists the comp-fidelity
build pipeline (comp-spec/comp-diff/build-phase/font-match and their libs),
the one Node toolchain that has not moved into the engine.
- Drop skill/scripts/live/project-ignores.mjs, lib/live-path-globs.mjs, and
their test: they import hook-lib/live-inject/impeccable-paths, which the
swap deleted, and their consumer (the JS live server) is the engine now.
- skill text: the comp pipeline's calls to engine verbs (generate-image,
embed-prompt) use the launcher spelling.
- Oracle: re-record 17 detect goldens over the fixture set main changed
(oklch #592, color-mix #578, 1D grid #615, the two comp-fidelity rules)
and record the gap in DELTAS.md; those JS rule changes are not yet ported
to the engine, and the goldens pin its current behavior.
bun run test (oracle included) and bun run build are green on this tree.
AI-assisted change: implemented with Claude Code.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaJv2c4oN8wS7Ttq4XRqyx
* Launcher: engine-probe PATH validation, working .cmd download path; CI: drop stale path, add oracle job
Byte-identical copies of the engine repo's launchers (engine main
af7572c): the retired 3.x npm CLI on PATH or in ~/.impeccable/bin is
rejected by the engine-probe handshake instead of hijacking every verb;
impeccable.cmd's download path is rewritten as straight-line goto flow
(the parenthesized blocks expanded %url%/%cached% at parse time, making
it dead code) with certutil sha256 verification and a windows-arm64 ->
x64 asset fallback; the final error points at the release download
instead of npm i -g (npm still serves the 3.x CLI).
ci.yml: the generated-output check no longer diffs the deleted
cli/engine/detect-antipatterns-browser.js, and a new oracle job fetches
the pinned engine (bun run fetch:engine) and replays tests/oracle/
against it. The job is continue-on-error with a loud warning until the
first engine release exists; flipping it to required is a release-time
toggle, documented in the workflow.
Verified here: sh -n on both launcher copies, bun run build green, full
oracle replay against the rebuilt engine binary green (770 pass, 0
fail), and a launcher behavior test proving a fake 3.x CLI on PATH is
skipped while the download + checksum chain completes against a local
file server.
Prepared with AI assistance (Claude Code).
* Oracle: restore detector goldens to post-fix behavior after the engine ports
The Aug 17-31 detector fixes (oklch parsing, color-mix nested hex, 1D grid
pass, comment stripping, root-relative linked stylesheets, URL userinfo
redaction, inert ignore-value refusal) and the comp-fidelity rules
organic-clip-path / buried-raster are ported to the engine. Re-records the
gap-pinning detect goldens from the fixed binary (glow.html included: its
.photo-opaque-grad column now carries the buried-raster finding it was
written for), replays the frozen checkHtmlPatterns call vectors through the
last JS engine state in history (db1462b9^; args untouched, 14 of 101
results moved), and rewrites the DELTAS gap section into the landed-ports
note. Each re-recorded json fixture golden byte-matches that JS state's
output; oracle: 770 pass, 0 fail.
Prepared with AI assistance (Claude Code).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaJv2c4oN8wS7Ttq4XRqyx
* Oracle: pin the Aug 17-31 verb fixes ported to the Rust engine
New cases: hook-session-grok-edit-then-stop (Grok Build camelCase envelope,
end_turn/shutdown/stopHookActive Stop handling,
|
||
|
|
8c59bc7fb0 |
Centralize image prompt parsing (#641)
Centralize raster prompt lookup and PNG chunk parsing while preserving read, scan, replacement, and sidecar behavior. AI-assisted merge: reviewed and executed by Codex under explicit interactive maintainer direction. |
||
|
|
377fb112b0 |
Pass the session key from detached idle-grace tests
Main's #469 tests POSTed /heartbeat and /answer without ?key=, which the gate now rejects, so those daemons looked dead. The e2e heartbeat counter also has to match pathname rather than a suffix, now that the URL carries the key. Written with AI assistance under maintainer direction. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
3c6f53406b |
Fix: stop the direction page hanging forever after a re-roll (#469) (#530)
* Fix: stop the direction page hanging forever after a re-roll (#469) The re-roll leg of the decision-page protocol was documented only in serve-question.mjs's own header, so agents never ran --update and the open tab polled a round that could never arrive. Compounding failure modes: the page poll swallowed every error, the daemon's --timeout was an absolute guillotine that killed the server under a still-open tab, a choice posted to a dead server confirmed nothing, and refresh or Reload on an unresolved round resurrected heartbeats that held the daemon alive indefinitely. - new-work.md documents the re-roll leg: rerun concept-seed with --from/--reroll, deliver with --update on the same key, never --start a second server. - The page poll terminates and says why: eight consecutive fetch failures means the server is gone; the delivery deadline (the server's own --idle-grace, inlined into the page) passing means the hand never arrived. Both stop heartbeating. - The daemon's --timeout bounds only the wait for a page to open; once the page heartbeats, the server lives while the page does and exits after --idle-grace (default 600s) without a beat, including under --timeout 0. - Build this and Re-roll against a dead server fail loudly instead of silently swallowing the click. - The server tracks the window between a collected re-roll answer and the --update that replaces the round, and serves the page in waiting mode there, so a native refresh re-enters the same bounded wait instead of resurrecting dead cards; the in-page Reload button only revives a delivered hand. - --update is exempt from the headless gate and its liveness probe trusts a fresh heartbeat over a failed kill probe (sandbox EPERM is not death). Squash of the six review-round commits on this branch, rebased onto main after the decision-page revamp. AI assistance: prepared with an AI agent operating under maintainer instruction (abdulwahabone). Co-authored-by: Cursor <cursoragent@cursor.com> * Fix review findings: persist the replacement deadline, refuse unloadable hands A browser-native refresh of the waiting page re-entered the bounded wait with a fresh delivery deadline and an immediate heartbeat, so refreshing before each deadline expired could hold the daemon alive and keep --wait on WAITING indefinitely. The server now records when the re-roll or followup answer was collected, each served waiting page inherits only what remains of that one allowance, and a page served after the deadline renders stalled immediately and never starts its heartbeat. And a next hand the round could not load used to reload-loop the tab: GET /'s catch kept the file on disk, so /next-status stayed ready:true forever. --update now refuses a payload without a non-empty options array at the sender, and GET / discards an unloadable next file so the bounded wait resumes. AI-assisted (Cursor agent) under maintainer instruction. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix review finding: a stalled page recovers a late hand without a click The stall silenced heartbeats so the idle grace could reclaim the daemon, but that silence read as a closed tab: after a late --update, --wait saw the stale beat and reported PAGE CLOSED while the user sat on the Reload screen, so the agent abandoned the browser path the recovery UI exists for. The stall screen now keeps a beat-free /next-status watch that reloads into a delivered hand on its own (GET never beats, so an abandoned flow is still reclaimed), and --wait no longer concludes closure from a stale beat while an undelivered next hand sits on disk. AI-assisted (Cursor agent) under maintainer instruction. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix review finding: a delivered hand must not mask a closed page The mid-delivery suppression keyed on the next file existing, but a closed tab never claims that file, so an unconsumed delivery held --wait on WAITING indefinitely instead of reporting the closed flow. The suppression is now age-bound: a stalled page's watch reclaims a delivered hand within seconds, so a file still unclaimed after a 10s grace means no page is coming back and the stale beat reads as the closed page it is. AI-assisted (Cursor agent) under maintainer instruction. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix review finding: stamp the delivery clock at --update, not the copy --wait's mid-delivery grace reads the next file's mtime, but copyFileSync's timestamp behavior is the platform's business: a copy that preserves the source payload's older mtime would start the grace already spent and report PAGE CLOSED under a live stalled tab. --update now touches the delivered file itself, so delivery time is delivery time everywhere. AI-assisted (Cursor agent) under maintainer instruction. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix review findings: disable canon during the wait, validate --timeout The waiting and stall screens disabled only the re-roll buttons; the footer canon action stayed clickable, and a canon pick posted after --wait had consumed the re-roll could never be collected: it overwrote the answer, marked the table closed, and exited the daemon under the agent. Both disable sites now take the canon exit down with the re-roll buttons; a delivered hand reloads the page and serves it live again. And --timeout reached the lifetime timer unvalidated: NaN or a negative value disarmed the no-page exit and the daemon leaked. It now takes the default unless the value is a finite non-negative number, keeping 0 as the explicit wait-forever. AI-assisted (Cursor agent) under maintainer instruction. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix review finding: a second click must not renew the delivery deadline dealAgain left the re-roll and canon controls live through the answer POST and the 700ms fly-out, so a second click posted another re-roll and the server restamped awaitingNextSince, renewing the deadline this PR made non-renewable on refresh and on the stall screen. The controls now go quiet at the click itself, in dealAgain and in answer(), and the server stamps the allowance only on the transition into the wait, so a duplicate answer racing the disable keeps the first stamp. Regression coverage on both sides: the unit deadline test posts a duplicate re-roll mid-allowance and asserts the budget shrank instead of resetting, and the e2e stall test asserts both controls are disabled immediately after the click, before the fly-out. AI-assisted (Cursor agent) under maintainer instruction. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix review finding: a late delivery must survive its claim window --update could land a replacement hand after the stalled page went silent but moments before the daemon's idle deadline: the daemon exited before the page's 1.5s watch could claim the hand, orphaning a delivery --update had confirmed, and the next --wait reported a server failure. The idle exit now defers while an unclaimed next hand is younger than the claim grace --wait already reads (extracted as one shared constant), so the page's watch deals it and heartbeats resume; a file unclaimed past the grace still ends the daemon, bounded as before. Regression test: deliver at idle-deadline-minus-a-beat, assert the daemon survives past the deadline and serves the late hand. AI-assisted (Cursor agent) under maintainer instruction. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix review finding: the claim itself must hold the daemon The idle-exit hold read only the next file's freshness, but GET / deletes that file when it serves the claimed round, before the reloading page can post its first heartbeat: a lifetime tick in that gap saw no pending hand and a stale beat, and exited under the hand just claimed. GET / now stamps the claim when it consumes a pending hand, and the idle exit honors the same bounded grace from that stamp, so the reloading page gets its seconds to beat while an abandoned claim still ends the daemon at the grace. The claim-window regression test now also fetches after the claim, past another lifetime tick, and asserts the daemon survived the gap; verified it fails on the previous commit. AI-assisted (Cursor agent) under maintainer instruction. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix review finding: --wait must ride out the claim gap too The claim deletes the next file --wait's mid-delivery grace watches, and the reloading page has not beat yet, so --wait in that gap read the stale beat as PAGE CLOSED while the daemon was alive serving the dealt round, and the agent abandoned a browser session that had just recovered. GET / now persists the claim stamp into the per-key state file, and --wait's suppression honors it under the same bounded grace: a fresh claim stays WAITING, a claim nobody followed with a beat still reads as the closed page it is. Regression test drives --wait through the gap (claim with a stale beat: WAITING, not exit 4) and past it (backdated claim stamp: exit 4); verified it fails on the previous commit. AI-assisted (Cursor agent) under maintainer instruction. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Paul Bakaus <paul.bakaus@gmail.com> |
||
|
|
f2f1cdb0bb |
Resolve every zoom target in one handler, and stamp each poll generation
Three review findings on #582, all real and all mine. Delegating `.pip` and `.media` to `document` separately meant they could not stop each other: stopPropagation ends bubbling, not siblings on the same target. Clicking the corner inspiration opened the inspiration and then the media handler replaced it with the comp, so the corner was unusable on exactly the cards this PR set out to fix. All three targets now resolve in one delegated listener in priority order, corner before chip before slot, and a chip that is not expand keeps its own click instead of falling through. Flip-back restored the face without clearing what the pending state had added, so a slot that reached stand-in came back carrying "comp pending" beside a fresh label, and one whose art had failed came back still marked unavailable. Restore now clears both, and a slot with no art to restore returns to the honest "artwork unavailable" treatment rather than being labeled inspiration. Converting in place means the same node is reused across flip cycles, and the old poll closure outlived its cycle: a probe from the first flip could settle the second one, stripping the new shimmer and stopping the live poll while the comp stayed hidden. Each run now carries a generation stamp that flip-back bumps, and both probe callbacks bail when it moves. The test covers the corner click against the landed comp, and I confirmed it fails when the priority ordering is removed. Written with AI assistance (Claude Code). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
ec189f4536 |
Flipping to comp demotes the inspiration instead of stacking a second slot
Two defects in the same few lines, both from `enterComp` hand-building a media slot after the deal instead of reaching the shape a comp-first render serves. A code-led card carrying catalog art shows that art as its face. Flipping to comp inserted a fresh shimmer slot above the body and left the face alone, so the card rendered the inspiration full-bleed with the rendering comp stacked under it: two images of equal weight, which is the one thing the corner treatment exists to prevent. The flip now converts that slot in place, moving the art into the `figure.pip` and dropping the face label, and flipping back restores it, so a round-trip leaves the card as it was dealt. The slot it built also carried no chips, and the zoom handlers were bound per element at load, so a comp that streamed in after a flip could not be opened at all: no expand affordance, and no click handler on the art. The three lightbox handlers are now delegated, which is what makes any later-built slot work, and a converted slot keeps the chips it already had. Polling learned to stop on a slot that stays in the DOM but loses its pending state, which only happens now that a flip back can restore rather than remove. The existing toggle test covered a wireframe card, where the schematic is hidden and a fresh slot inserted; that branch was fine, which is why this went unseen. The new test drives the art-carrying card and fails on the stacking assertion without this change. Written with AI assistance (Claude Code). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
504b8f2a22 |
Merge pull request #571 from pbakaus/codex/issue-565-sketch-timeout
Fix stalled missing decision comps |
||
|
|
76b9aaf021 |
Build-path toggle moves to the header; a code-to-comp flip confirms first
The toggle sits top-left under the brand instead of in the footer bar, and flipping to comp-first now opens a confirm dialog before anything renders, since the flip starts billed, minutes-long generation; flipping back stays free and immediate. The dialog lives at the document root so it never loses the stacking fight with the deck. The schema blob also states harder that toggle: true may only be offered when image generation exists. Written with AI assistance (Claude Code). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
64dd60a78a |
Build path becomes a setting plus a page toggle; the followup contract round retires
- serve-question: payload buildPath { value, toggle } renders a footer
segmented control (comp first / code first) with the trade stated in one
line; the default comes from settings, a flip binds that session only.
Code-led rounds treat declared comp paths as flip reserves: wireframes
render, a flip to comp shimmers the slots and surfaces once through
--wait as BUILD PATH FLIPPED so the agent starts generating mid-round;
the flip back is free and a landed comp stays. The ANSWER carries
buildPath and buildPathFlipped with a session-only directive.
- init Step 5 asks the preference once (only when image generation exists)
and writes .impeccable/settings.json; context.mjs surfaces the recorded
default every session; PRODUCT.md standing commitments stay honored as
the fallback.
- new-work retires the two-card execution-contract round: no round asks a
workflow preference. followup stays as the generic same-table mechanism.
- e2e: new toggle test (14/14 with the wireframe test).
Written with AI assistance (Claude Code).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
ac4c3200db |
Surface rounds deal three structures, draw wireframes, and anchor comps on a reference screenshot
- concept-seed --scope surface deals three grounded-list indices (dice-picked, primary leads) instead of one: a single card is not a choice, and the no-lineup rule stays direction-only, where it was written for worlds - serve-question renders a new per-card wireframe field as a layout schematic in the media slot: the code-led channel's visualization, no image generation needed, no card back, no salience weight - generate-image gains --ref (repeatable): routes through the edits endpoint with input images, so an established world's comp inherits identity from a captured screenshot of a real page instead of a prose paraphrase; tested against impeccable.style, where the reference-anchored comp reproduced the live site's chrome and the prose-only comp drifted - new-work rung two rewritten around the dealt hand: lock-in is the approval, a locked comp builds comp-led and discharges the visualize.md three-option round, a locked wireframe builds code-led; visualize.md records the exemption and the reference-image discipline, including the reference-leak caveat (chrome carries, the reference page's content does not) Written with AI assistance (Claude Code). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
f66eace20d |
Decision page: plain-language raises, IMPECCABLE'S PICK, sticky footer, short-viewport fit
- The raise block drops the side-tab left border for a quiet patina panel, and drops the poker jargon: "Improved by Impeccable's worlds" with per-line "From <world>" donors, on single raises too; tooltip, aria, and screen-reader copy follow - The pick-card kicker convention renames MY PICK to IMPECCABLE'S PICK at every definition site, so users stop reading "my" as themselves - The footer (steer, registers, canon exit) is a sticky full-bleed bar on wide viewports, sharing one --page-inset with the content column; portrait keeps it in flow where the deck scrolls internally - Short landscape viewports compact the headline and narrow the cards so a full round fits 1440x800 Written with AI assistance (Claude Code). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
3a26dcb809 |
Keep decision body order in fallback
Prepared and verified with AI assistance under maintainer authorization. |
||
|
|
37be3fa36b |
Fix stalled missing decision comps
Restated on current upstream main after the comp-field migration. Prepared and verified with AI assistance under maintainer authorization. |
||
|
|
248a4a699a |
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 <noreply@anthropic.com> |
||
|
|
dbff0880e6 |
Decision page: full-fidelity comps, raise cycler, declined sizing, canon order, full card anatomy (#545)
* Polish the decision page: raise cycler, declined height, canon order, full card anatomy Field feedback from the first real rolls of the verdict-routed hand: - Several raises stacked on the assigned card blew it out of proportion. More than one raise now renders as a compact cycler: one visible, a counter, click or Enter advances. A single raise stays inline. - Declined cards inherited the row's stretch alignment, so a narrow card stood at the tallest contender's height, a strange stilt beside the hand. They now size to their content. - Deck order becomes a gradient of standing: contenders, then the canon, then declined dead last. The canon between full alternates and the demoted row reads as the familiar door rather than the last resort after the rejects. - Root cause of bare-bones challenger and canon cards in the field: the --schema example only gave the assigned card palette, materials, and risk, and models author payloads by imitating the example, so the "same anatomy on every card" instruction lost to it every time. The example now carries full anatomy on every card and the schema note says a card with no palette chips is an authoring gap, not a data gap. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * Decision cards carry full-fidelity comps instead of sketches Field verdict on the sketch contract: the sketches came back too simple to inform the choice, and generation takes the same time at any fidelity, so the deliberately-unfinished frame paid comp cost for sketch quality. The decision card's image is now that direction's north-star comp, produced under visualize.md's comp discipline (structure-led prompt, real name and content, no invented commercial claims), saved under .impeccable/mocks/ with its prompt sidecar. Fairness between cards comes from equal fidelity in each card's own grammar rather than shared unfinishedness. The chosen card's comp is never spent by the choice: on a comp-led build it enters the comp round as compositional option one (visualize.md now generates two variations beside it; a round arriving with no decision comp still renders all three), and on a code-led build it returns at the finish review as the critique reference. Produce order still front-loads a re-roll's spend onto the cards read first. serve-question keeps the sketch field's wire name for payload compatibility; docs, schema paths, shimmer labels, and the answer directive (CHOSEN COMP) speak comp. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * fix: address PR review bot findings on the comp round - Producer still forced sketches (cursor, high): the asset producer's Decision Sketches contract still mandated deliberately unfinished matte sketches, so the parallel path would keep shipping sketch-era images. The section is now Decision Comps: full-fidelity north-star comp, structure-led prompt, equal commitment across siblings, no invented claims, sidecar written. - Mocks collided with the approval check (cursor, high): decision comps now live under .impeccable/mocks/decision/, visualize.md scopes the no-approval finding to comp-round output, new-work.md states the unchosen hand implies no approval, and the code-led finish packet names the chosen decision comp as the critique reference in the approved-comp slot. - Raise cycler announces (greptile, both P1s): a visually hidden aria-live region reads out the newly active raise and its position on advance; initial render stays quiet. - Declined width in the vertical deck (cursor, medium): align-self: flex-start shrank declined cards to content width in the portrait column layout, where the cross axis is horizontal; they stretch there and keep content height in the row layout. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * fix: raise cycler tooltip and label name both input modes Copilot: the tooltip said Click while the control also answers Enter and Space; the title and a new aria-label now say activate/press Enter. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * fix: finish reviewer exempts decision comps from the approval check cursor[bot] follow-through: the reviewer's Persistence check still treated any comps under .impeccable/mocks/ as approval-gated, and the reviewer never reads visualize.md by design, so code-led and spent-hand rounds could draw a false skipped-approval finding. The check now scopes to comp-round comps, exempts .impeccable/mocks/decision/ as the direction round's dealt hand, and defines how a code-led build's decision comp is judged in the approved-comp slot: the critique reference, under the no-approved-comp fidelity rules plus what the image dared that the build did not. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * fix: the critique reference is its own reviewer input, not the approved-comp slot cursor[bot]: passing the code-led decision comp through the approved-comp slot dragged in that slot's obligations (inventory-first reading, the fidelity matrix, Truth's shipped-asset demand for every image-native region), which contradicts code-led's premise. The input contract now names it a separate labeled critique-reference input that nothing binding "the approved comp" touches, and Fidelity defines its treatment where the no-approved-comp rules live: provocation, not spec; no matrix, citations, or asset obligations; its dares enter material_fixes as ordinary fixes. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> --------- Co-authored-by: Claude Code <noreply@anthropic.com> |
||
|
|
c70bcbf6b4 |
Direction round: verdict-routed hand, MY PICK card, salience parity, Safer/Bolder registers (#531)
* Route the direction hand by verdict, add the pick card, enforce salience parity The decision round previously rendered every dealt challenger as an equal full card whatever the weighing said, so a world that fused poorly (an underwater world dealt to a flower shop) sat at the same visual weight as the assigned direction, and concept-level fusion had no surviving output. Three changes, all presentation-layer; the dice, the assignment, and the two-axis weighing are untouched: - Verdict routing: the weighing closes with wins / competitive / declined per challenger, decided before any borrowing. Declined challengers render demoted (narrow, quiet, catalog art as a labeled thumb, "Adopt anyway"), reordered to the end of the deck by the page itself, still adoptable, never silently dropped. Donations return as named "raised by" lines on the assigned card: a declined challenger donates ambition and system discipline, never its clothes. - The pick card: one card for the model's top-ranked grounded candidate when the dice assigned another, kicker MY PICK, honest familiarity risk on its face. One card, never a ranked list, never the lead position; the anti-menu rule survives with exactly this carve-out. - Salience parity: a card's imagery weight is capped by the assigned card's. With a text-only assigned card (no image generation in the harness), full-bleed catalog heroes demote to labeled thumbs, so what looks important is the verdict's call, never rendering luck. serve-question payload gains additive fields (verdict, kept, raised); old payloads render unchanged. concept-seed's rendered instructions carry the verdict/donation contract and the pick-card carve-out. Covered by two Playwright tests in the new-work e2e suite (verdict routing + parity). Design exploration and rationale were worked through with the maintainer; research grounding is impeccable.style/research lessons 3-5. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * Add Safer/Bolder re-roll registers to the direction round The re-roll gains the user's steering wheel on the familiar-to-bold axis. The decision page renders two register buttons beside the plain re-roll (payload: reroll: { registers: ["safer", "bolder"] }; booleans still work), the answer carries the chosen register, and concept-seed gains --register. The design constraint that shaped the implementation: a register changes only what a round INSTRUCTS, never what it DEALT. The same key and reroll count reproduce the same deal whatever the register, so the exclusion chain never forks and the reproduction contract holds with no API change. - bolder: the dealt foreign forms become the whole hand, every challenger a full card; the first-dealt challenger leads (assignment by deal order, so the dice still choose). The pick card sits out; the canon stays. - safer: the round's dealt hand is spent unseen and stays excluded; the model presents its remaining conventional grounded candidates (at most three) plus the canon executed against named competitors. This is the one sanctioned lineup of the model's own ranked list, existing only by explicit user request. Works degraded (needs no catalog); bolder degrades to a plain grounded round, disclosed. Registers are user steering, never the model's to pre-select. Covered by a concept-seed unit test (same-deal invariant, validation) and a Playwright test (button, answer field, REGISTER directive). AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * Add the execution-contract round: comp-led or code-led, chosen after the direction The build previously went comp-led for everyone, silently: a generated comp led and the build chased it, which produces the boldest compositions and also the measured worst-of-both-worlds failure (ambitious design landed poorly, no motion, fix rounds after). Models already defect from it by quietly skipping comp generation, which is unsanctioned code-led with no contract to catch it. This makes the fork explicit and both paths defection-proof: - Comp-led: the comp is law and non-optional once chosen; visualize.md and the comp-is-king build phases run as today. - Code-led: no comp of this page, skipped by contract rather than drift. The QUALITY BAR boards still calibrate finish, and the ambition moves into the written direction contract (FIRST VIEWPORT plus a named signature interaction and motion grammar), audited by the finish reviewer in behavior. Not a discount on commitment. Placement: a second round on the same open table, right after the direction lands. Sketches stay in the direction round (they pick the world); comps are what code-led skips (they bind the composition). The chosen world sets the default lead; the user flips freely; a standing preference recorded in PRODUCT.md skips the round on later surfaces; with no image generation there is no fork, code-led is the only path. Mechanism: serve-question gains payload-level followup: true, which keeps the detached server alive after a pick (exactly like re-roll), swaps the page to the loading hand instead of goodbye, marks the answer with followup: true so --wait keeps the table, and prints a FOLLOWUP OPEN directive telling the agent to deliver the next round via --update. Covered by a Playwright test driving the full two-round flow. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * fix: address PR review bot findings - Degraded safer register no longer contradicts itself (greptile, Copilot, cursor): the degraded template previously said "the assigned index is suspended; the user picks" and then emitted ASSIGNED INDEX, the mandatory build instruction, and the restated footer anyway. The degraded safer path now suppresses the assignment machinery entirely, matching the non-degraded safer round, and restates the user-picks behavior for truncated readers instead. - A declined card's declared sketch no longer renders a full media face (Copilot): the renderer ignores sketch slots on declined cards outright, so a stray sketch cannot buy back the salience the verdict took away. - Bolder rounds no longer carry the generic weighing instruction (cursor): it measures against the assigned grounded direction, which the bolder register suspends; a leader-relative variant weighs the fused challengers against the first-dealt leader instead. All three pinned by new assertions in tests/concept-seed.test.mjs and tests/new-work-e2e.test.mjs. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * fix: followup never arms the loading hand in blocking serve mode cursor[bot] caught a client/server disagreement: the page interpolated its FOLLOWUP constant from the payload alone, so a followup: true payload served in blocking mode (no --start) would leave the browser on a loading hand that nothing resolves, since a blocking server exits on any pick and has no update channel. The page constant is now armed only when the server is detached, blocking rounds get the goodbye screen as before, and new-work.md states that followup belongs only on a detached round; blocking and structured-tool channels run the build-path round as its own second question. Pinned in tests/serve-question.test.mjs. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * Add card-kind choice telemetry and the bolder routing disambiguation The choice ping previously fired only when a dealt catalog challenger won, so pick-share and canon-share had no denominator and the decision page's new spectrum could not be measured. The ping now fires once per resolved attended round on API-dealt rolls: --kind names which card class won (assigned / pick / challenger / canon), --chosen carries the catalog id only when a dealt challenger won, and --register rides along when the round came from a steered hand. Grounded candidates' names never leave the machine (the ping carries the kind alone), the legacy id-only shape stays valid, and DO_NOT_TRACK / IMPECCABLE_NO_TELEMETRY still disable the ping entirely. The seed's TELEMETRY block teaches the new invocation. Also the naming-collision guard: "bolder" said while a direction round is open routes to the Bolder hand register, never the bolder refinement command; one line each in bolder.md and new-work.md. The /api/chosen field additions land in a sister impeccable-site PR; the API ignores unknown fields meanwhile, so this is safe to ship first. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> * fix: ping test survives a DO_NOT_TRACK shell cursor[bot]: the pingChosen unit test cleared only IMPECCABLE_NO_TELEMETRY, so a developer shell with DO_NOT_TRACK set failed the success-path assertions. The test now clears both, restores prior values in finally, and passes under DO_NOT_TRACK=1. AI-assisted change. Co-Authored-By: Claude Code <noreply@anthropic.com> --------- Co-authored-by: Claude Code <noreply@anthropic.com> |
||
|
|
1045c6ca98 |
Gracefully handle the no-image decision page (#502)
* Gracefully handle the no-image decision page Tested the new-work path without image generation and fixed what broke: - A text-only card's back face (First viewport, The case) was unreachable: the Details flip chip only rendered inside the media block. Cards with no imagery now render their full read on the front and skip the back face. - A hero/board that fails to load (retired catalog URL, offline shell) sat as a dark void with a zoom cursor. The slot now collapses to a field painted from the card's own palette with an "artwork unavailable" pill; broken inspiration PIPs remove themselves. - Sketchless catalog art rendered unlabeled as the card's face, reading as the promise of the build. It now carries the same "inspiration" label and hover title the PIP uses. - The --schema example pointed at catalog URLs that 404 (missing family prefix); updated to the real asset paths and noted the text-only front behavior in the schema prose. Extends e2e test (e) with the front-read and label assertions and adds test (f) for the broken-image fallback. AI-assisted (Claude Code). Co-Authored-By: Claude Code <noreply@anthropic.com> * fix: address PR review bot findings - cursor[bot]: the unavailable-art scrim painted over the flip chips and swallowed their clicks; it now passes pointer events through and the chips render above it. - Copilot: a palette-less card whose art failed still read as a dark void and kept the stale Inspiration tooltip; the slot now falls back to the graphite field in CSS and the tooltip is removed with the art. Test (f) now covers both: a broken card with back facts must still flip via Details, and a palette-less broken card gets the labeled fallback. AI-assisted (Claude Code). Co-Authored-By: Claude Code <noreply@anthropic.com> --------- Co-authored-by: Claude Code <noreply@anthropic.com> |
||
|
|
6ece0e588f |
Add deterministic new-work interactive smoke suite
A cheap, LLM-free E2E tier for the interactive parts of new-work, mirroring the two-layer live-e2e pattern (deterministic now, opt-in LLM tier later). - generate-image.mjs: IMPECCABLE_IMAGE_GEN_FAKE=1 writes a deterministic offline image (SVG with wrapped prompt + SYNTHETIC COMP label, or a valid palette-stripe PNG carrying the prompt/marker in a tEXt chunk). Same CLI contract, no key, no network, $0.00 cost line. - tests/new-work-e2e/user-bot.mjs: scripted user bot (module + CLI) that resolves the serve-question daemon from the workspace and drives the real page via Playwright (pick, re-roll + steer, canon, tab close). - tests/new-work-e2e.test.mjs: node --test coverage of the serve-question cycles (pick + CHOSEN CARD, re-roll + --update re-deal, canon + CANON CHOSEN, tab-close exit-4, text-only card) plus fake image determinism. - Registered as the opt-in new-work-e2e suite; added test:new-work-e2e. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |