Commit Graph
635 Commits
Author SHA1 Message Date
Paul BakausandClaude Fable 5.1 d1f81fc1c7 A rendered line is the whole line: the fragments merge, the snapshot carries them or the rule stands down, an ignored subtree gets no vote in the palette, and a class a design document forbids declares nothing
The review threads on #840 found four ways the new rendered rules were
still measuring something other than what the reader sees.

- `line-length` read one `getClientRects()` box per direct text node and
  called each of them a rendered line. An inline `<strong>` in the middle
  of a sentence is its own text node, so one visual line arrived as two
  fragments and the paragraph's characters were divided between them —
  and the `<strong>`'s own text was never measured at all while its
  characters still counted toward `text_len`. Both halves of the measure
  are the same text now: the probe collects the rects of every text node
  under the element, and `Dom::text_line_rects` merges the ones that share
  a row back into the line they rendered as. A column of long lines split
  by inline markup used to charge nothing; it charges now, and a wrapped
  sentence in two fragments is one line, not two.

- The same function divided every rect by the line box to recover a line
  count. On live per-line rects that was double-counting: a leading
  tighter than the glyph box makes `round(height / line_box)` 2, and one
  long line pushed twice satisfied "at least two long lines". Nothing is
  divided any more, because nothing that reaches the rule is a union.

- The union was what a snapshot-backed scan had — the extension's
  offscreen document and any strict-CSP page — and a union of a long
  first line and a short tail is the same union as two even lines, so
  every line inferred from it was invented. The capture records the rects
  now (`dl`, with `textLines` on the snapshot saying it did), and a
  capture that did not answers `None`: the rule stands down rather than
  guessing. That is also what any other DOM that cannot split a wrapped
  run answers.

- `ai-color-palette` accumulated its tell hues before the scoped ignores
  ran. A cyan tell inside a `data-impeccable-ignore="ai-color-palette"`
  subtree opened the page-wide two-hue gate and charged neon ink on an
  element nobody had waived. Ignored content now gets no vote.

- `declared_component_selectors` took every backticked class in DESIGN.md
  as a declaration, including the ones the document writes down in order
  to forbid them. "Do not write a new `.hero-cta-primary`" exempted
  `.hero-cta-primary` from `kicker-above-heading` — the parser silencing
  exactly the misuse the document was written to catch. Each occurrence
  is read in the document's own structure now: the heading chain above it
  (a "Don't" section, and its subsections with it) and the clause it sits
  in, where a clause is cut on punctuation and on the phrases that turn a
  sentence around. "No ALL CAPS outside the `.eyebrow` class" still
  declares `.eyebrow`, because what "no" governs ends at "outside"; a
  class the document calls deprecated anywhere is declared nowhere.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQBUunp8QttxZqihybNmtL
2026-09-20 19:02:46 -07:00
Paul BakausandClaude Fable 5.1 a5df2b0826 The rendered rules measure what rendered: the line instead of the box that holds it, the space around the text instead of the declared padding, gray by chroma at its own lightness, the AI palette by two tell hues rather than one accent, and the eyebrow named as the element its own design document declares
Five browser rules were reporting something other than what the reader sees,
and a review that charges those numbers is charging noise.

- `line-length` measured `rect.width / (fontSize * 0.5)`, the box's capacity.
  A paragraph in a 1022px column whose text stops at 571px was charged 142
  characters a line it never rendered. The probe now hands back the client
  rects of the direct text one per line box (`direct_text_line_rects`), the
  characters divide between the lines in proportion to the ink each carries,
  and the charge needs more than one long line: the harm named is the eye
  tracking back to the start of the next line, which takes a column to do.
- `cramped-padding` read the declared padding. A 44px control with
  `padding: 0 16px` and a flex-centred label has 12px of air above the label
  and was charged "0px vertical padding"; the measurement is now the inset
  between the rendered text and the inside of the border box. Its wrapper half
  read a text-bearing child's border box the same way, so a `<td>` that fills
  its table and insets its own text counted as flush; it reads the text now.
- `gray-on-color` called anything under 0.85 relative luminance gray, which
  takes in every off-white: `#e8edf2` measures 0.84 there and 0.93 as
  lightness. Gray is now low chroma at the lightness the ink actually sits at
  (saturation, which is chroma normalized for lightness) and neither of the
  two neutral inks a coloured surface carries. The contrast check beside it is
  untouched, and the recorded vectors still pass.
- `ai-color-palette` charged every hue between 160° and 200° on a dark ground
  as neon, which lit one ordinary teal accent 18 places on a page with nothing
  wrong with it. A gradient in a tell hue is still the pattern on its own;
  flat neon ink on near-black waits for a second tell hue to turn up somewhere
  on the page, because one saturated accent on a dark system is an accent.
- `kicker-above-heading` reported against `body`, so a charged row had nothing
  to point at, and it fired on eyebrows a design document documents. It names
  the eyebrow element now, and stands down where the repository's DESIGN.md
  declares the class by name — the prose's backticked class selectors travel
  on the design-system config the colour and radius rules already read.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQBUunp8QttxZqihybNmtL
2026-09-20 18:53:36 -07:00
f2c7051853 Fix: drop discard blank lines from live-accept oracle goldens (#793)
The four discard goldens encoded the wrapper newlines as empty lines around the restored element. Re-recorded from the engine so they match the trim in extract_original.

Written with AI assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-16 17:57:54 +05:00
71a3341289 bake: the anchor must have matched one element on the page
The lasting rules a bake appends apply to every element the anchor
matches, so a `tag.class` anchor shared by siblings (three cards from one
JSX element, say) restyled all of them, not the element the user picked.
The overlay now journals, with the generate event, the anchor it would
bake on (`element.anchor`: the id, else the tag with its classes) and how
many elements matched it when Go fired (`element.anchorMatches`). The
planner bakes only when the source anchor is that same selector and the
count is one; otherwise it leaves the carbonize block with the count in
`bakeSkipped`, and the agent integrates the variant by hand.

Written with AI assistance (Claude).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
0166cbb870 tests: the agent-target suite arms the live-server reaper
The suite spawned its live servers directly and stopped them in after()
hooks only, so a run killed mid-test could leave them behind. It now arms
the shared reaper and tracks each child like the other live suites do.

Written with AI assistance (Claude).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
695d1bd515 generate: plan, tune, and accept exactly like live
The lane's variants were tamer than the ones a live session makes on the
same element: its poll instructions replaced live.md's planning method
with a cheat sheet, its reference forbade knobs, told the agent to copy
the markup verbatim and to treat DESIGN.md as a hard boundary, and its
accept appended anchored overrides instead of integrating the design.
Measured on the same page with Opus, live runs promoted a tier, broke the
grid, and declared knobs; lane runs restyled three equal boxes.

Now a Go the generate verb fires gets the same _instructions as a user's
Go (the action's reference, section 4 planning, knobs per section 7),
generate.md hands the design work to live.md's Handle generate and its
Required after accept, Setup runs as for any command, the Tune chip
behaves as in any session, and the mechanical bake is opt-in (--bake)
instead of the lane's default. The start verdict points at live.md, and
`browser` (the config key the opener reads) is a recognized key.
Goldens re-recorded for the accept help and the recognized-keys line.

Written with AI assistance (Claude).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
496b386504 overlay: a lane session shows the variant bar and nothing else
The generate lane hides the helper's global bar, but the overlay drew it
first and hid it on `connected`, the Tune chip spun for the seconds
between the variants mounting and the done reply even though the lane
declares no knobs, and the agent-target pick rendered the edit-copy
pencil with its "disabled while applying" tooltip. The served script's
prelude now says when the bar is hidden so it is never drawn; a session
records who fired its Go (`sessionOrigin`, saved with the session) and
never shows the pending Tune chip when the generate verb did; the
agent-target pick suppresses the edit-copy badge for the session. A
user's own pick, Go, and session are unchanged, and the source test pins
every gate.

Written with AI assistance (Claude).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
e06c152ad2 generate lane: one-shot start, event in hand, mechanical bake
The generate command's fast lane spent most of its time on agent round
trips, not on the engine. Four engine changes take them out, all behind
the lane's own flags so a plain `live` session is untouched:

- `live-poll --reply <id> done --then-poll` replies and waits for the
  next event in one call; the reply's ack rides along as `_replyAck`.
- `live-generate` collects the session's own generate event into its
  output (`GET /poll?types=generate&id=<sessionId>`, a new id filter
  the parked-poll flush honours too), so the pickup poll is gone.
- `live-generate --boot` runs the lane's boot in-process and reuses a
  running helper; `--dev-url <url>` names the dev server the agent
  already knows and leads the probe; with no page connected the verdict
  is `browser_needed` with the harness's own way to open the page
  (Cursor browser_navigate, Claude Code's Browser pane, Codex --open or
  the user). `--open` launches the system browser only on a harness
  without one: on cursor and claude-code it is ignored unless
  IMPECCABLE_BROWSER or the config's `browser` names a browser, so a
  second window never opens beside the harness's. The served /live.js
  carries the helper-wide bar preference in its prelude.
- The accept of a session the generate verb started (journaled with
  origin "agent", or `--bake`) is baked mechanically: the accepted
  variant's @scope rules are re-anchored on the element's own selector
  and appended to the stylesheet that names it, the wrapper is
  unwrapped, the source verified clean. Knobs, plumbing inside the
  variant, no stylesheet, or a selector the rewrite cannot decide fall
  back to the carbonize block with `bakeSkipped`. `--no-bake` refuses.

Goldens re-recorded for the three help texts and the no-browser case.

Written with AI assistance (Claude).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
65c8089182 Only the lease holder may answer an agent target; leave a plain bar alone
Review found that /agent-target-result checked the shared helper token
and nothing else, so any connected overlay could resolve a target it
never claimed. A result post now names the overlay (`clientId`), and
while the target is pending only its lease holder's word lands: a
bystander gets 409 (not_holder, or unclaimed when nobody holds it) and
the request stays pending. The overlay sends its client id with every
result. Every protocol case now answers from the tab that actually
holds the claim.

Also: the helper-wide bar preference is applied on every connected
frame, and restoring wrote an empty display value, which dropped the
bar's own inline flex layout for a plain live session that never asked
for anything. Hiding remembers the bar's display value, restoring puts
exactly that back, and a restore on a visible bar is a no-op. A plain
boot through the launcher keeps display: flex after connect and its
payload carries none of the lane's keys.

Written with AI assistance (Claude).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
1a699913e4 Make the generate lane's bar preference helper-wide
The maintainer still saw the bottom bar: two tabs were connected to the
helper, the tab that won the roll call hid its bar, and the tab on
screen never did. The hide was also applied only at Go, so the wait
before it showed the bar as well.

The helper now owns the preference. `impeccable live --no-live-bar`
posts `/live-bar` right after the helper is up, so the bar never
appears in any tab; an agent target carrying `hideLiveBar` sets the
same flag before the target goes out. The helper broadcasts
`live_bar` to every connected tab, answers `hideLiveBar` on every
`connected` frame (reloads, later tabs) and on `/status`, and the flag
lives as long as the helper. The overlay just follows: no per-tab
memory, no session scoping, the variant controls still show. The same
preference skips the overlay's "No PRODUCT.md found" connect notice,
which sent the user to init inside a lane that runs without context by
design.

Verified in a real Chromium session with two tabs, screenshots at each
stage: idle (bar in both), after Go (bar gone in both), after reloading
both, after the accept during the bake, after a reload after that.

Written with AI assistance (Claude).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
7c42d0feba Keep the live bar hidden for the helper's lifetime, not the session's
The maintainer still saw the bottom bar on the generate lane: the hide
was released the moment the accepted session ended, which is the start
of the agent's bake, so the bar sat there for the minute until the
helper stopped. The choice now lives in sessionStorage keyed on the
helper token: applied at Go, re-applied by every reload's bar rebuild,
kept through the accept and the bake, and forgotten only when the
helper stops and takes the overlay with it. The next `impeccable live`
boots with the bar again.

Verified in a real Chromium tab: hidden through a reload, the accept,
and a reload after the accept; the helper stop removes it entirely.

Written with AI assistance (Claude).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
e3a121d081 Generate lane: settle the Tune state without knobs, and hide the live bar on request
Two things the maintainer hit testing the lane.

The Tune chip spun forever after a generation whose variants declared
no knobs (the lane's default). The overlay flips the parameter phase to
pending at Go and only settled it when the wrapper mounted; the page
reloads on the JSX write, the resumed session restores "pending" from
its cache with the variants already mounted, and the agent's done reply
never re-checked. Now the done reply completes the phase once every
variant is mounted, and a resume with a pending state asks the helper's
session record whether that generation already finished. A generation
with no knobs shows no chip; one with knobs shows them.

`live-generate --no-live-bar` (body `hideLiveBar: true`, forwarded on
the agent_target payload) keeps the helper's global bar hidden for the
session it starts; the variant controls still show, the choice survives
a reload through the session cache, and the bar returns the moment that
session ends on any path. generate.md passes the flag.

Verified in a real Chromium tab: no chip before and after a reload, bar
hidden through the reload, bar back after the accept. Rust and protocol
cases for the flag, a CLI parse test, contract pins for both fixes.

Written with AI assistance (Claude).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
f29636a3d5 Keep the generate lane's boot extras behind flags
The speed pass had every `impeccable live` boot probe for the dev
server and print three new keys (devUrl, contextMissing, contextNote),
which moved an oracle golden and cost a plain live session a probe it
never asked for. The lane's extras are opt-in now: `--dev-url` runs the
probe and reports devUrl; `--allow-missing-context` reports the context
keys. Without either flag the boot's work and payload are byte-identical
to before, which the restored golden and a new boot test pin.

generate.md passes both flags; the contract doc says so.

Written with AI assistance (Claude).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
1220f26d08 Make the generate lane snappy: self-contained playbook, fast-path poll instructions
The maintainer's field run took five and a half minutes from the prompt
to variants on screen. Two baseline runs on the same repo reproduced it
(356 s mean): 68 KB of skill text read before the first variant (a 36 KB
live.md among it), six to ten tool calls spent finding the dev URL and
the selector, 9 to 10 KB of variants carrying tune knobs, and a document
read plus a detect pass after the accept.

generate.md is now the whole contract for the lane and never sends the
agent to live.md, craft-floor.md, or the action reference on the happy
path; the floors are inlined. The engine carries the rest: a generate
started by live-generate is journaled and queued with origin "agent",
and its poll instructions hand out the fast path (identity from the
event's computed styles and custom properties, the action's three
dimensions, no knobs unless asked, one edit, reply done) instead of the
interactive planning pointer. `impeccable live --allow-missing-context`
boots without PRODUCT.md or DESIGN.md, naming what is missing, so the
lane never falls into the init interview; the boot also reports devUrl,
the origin whose page carries the injected tag, so the agent opens the
page instead of reading terminals. Accept is a bake and live-complete is
its verification: no detect pass, no document read.

Three trimmed runs (one without any context files) averaged 179 s from
prompt to variants, 21 tool calls and 106k tokens against the baseline's
356 s, 30 tool calls and 144k tokens; the accept bake went from 67 s to
41 s. Method and numbers: tmp/questionaire/plan41-field-tests/SNAPPY-REPORT.md
in the maintainer's checkout.

Tests: dev_url probe unit tests, a fast-path instructions unit test, the
origin marker in the protocol suite, and tests/live-boot-fastpath.test.mjs
(flag, contextMissing, devUrl through a stand-in dev server); contract doc
updated.

Written with AI assistance (Claude).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
d579ecb2f2 Address review: a Go for a target the helper does not hold is refused
The bounded record of answered targets evicted its oldest entry, and a
generate event naming an unrecognized target was admitted, so a Go
delayed past enough later resolutions could still open a session for
a request the CLI had reported as failed.

The admission rule is now positive: a generate event naming an agent
target is welcome only while that target is pending without a rival
lease, or when it comes from the session that answered it. Unknown
targets, evicted or never issued, are refused like any other superseded
Go, so eviction can never reopen a request. The record keeps 256
entries for the answering session's sake.

Tests: a Rust integration case and a Node protocol case (an envelope
naming an unheld target is refused and journals nothing; the same event
without an envelope is an ordinary Go); contract doc updated.

Written with AI assistance (Claude).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
76db418212 Address review: every answered agent target fences a late Go
Only targets answered with a session were fenced against a delayed
generate event. A request that timed out (or ended in another failure
verdict the CLI already reported) was simply forgotten, so a Go whose
capture outlasted the timeout still opened a session nobody was told
about.

`resolve_agent_target` now records every terminal resolution, with the
answering session when the verdict carried one, and
`agent_target_refusal` refuses a generate event for any answered
target unless it comes from the answering session itself. The
browser_timeout instructions no longer send the agent to live-status
for a session that can no longer start. The overlay's refusal toast
covers both causes.

Tests: a Rust integration case and a Node protocol case (claim, time
out, late Go refused with 409 and nothing journaled), a unit test for
the timeout instruction; contract doc updated.

Written with AI assistance (Claude).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
7adb81672d Address review: a superseded Go never opens a second session
An overlay renews its lease right before Go, then captures and uploads
the element before its generate event leaves. When that outlasts the
lease and its result post is lost, a rescuer can claim the target and
Go, and the helper accepted both generate events: two sessions for one
request.

The generate envelope now carries this page's clientId, and the helper
refuses a generate event for a target that another page holds under a
live lease, or that was already answered with a different session
(`served_agent_targets`, recorded on every ok resolution): 409
`agent_target_already_served`, nothing journaled. The overlay treats
that refusal like a foreign session and hands the surface back. The
answering session's own event stays welcome, so the common path (result
post first, then the event) is unchanged.

Tests: two Rust integration cases (rival lease, answered elsewhere,
welcome for the serving session) and a Node protocol case, contract
pins for the envelope and the refusal handling, contract doc.

Written with AI assistance (Claude).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
b5210471fb Address review: the generate event resolves the agent target it serves
A winning overlay could reload after handleGo() minted a session but
before its result post landed. The close released its lease, the
server replayed the still-pending target, and another tab (or the
reloaded page, once it abandoned the unknown session) could claim it
and fire a second Go for a request that already had a session.

The overlay now names the target on the generate event it fires for it
(`agentTarget: {targetId, result}`, the same result it posts), and the
helper resolves the pending request the moment that event is accepted,
stripping the envelope before journaling. Whichever of the event and
the result post lands first answers; a page that dies between Go and
its result cannot leave the request pending, and a request whose event
never reached the helper is served exactly once by the rescuer.

Tests: a Rust integration case and a Node protocol case (claim, Go
event without a result post, verdict carries the session, a late claim
finds nothing pending, the journal carries no envelope), contract pins
for the handoff, and the contract doc.

Written with AI assistance (Claude).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
335945525d Address review: a done target stays off-limits for a replay
Marking a target done on reply opened a window: an EventSource reconnect
replays the still-pending target while the result is on the wire, the
tab is GENERATING by then, so it declined busy, the server handed the
lease back mid-resolution, and another tab could claim and fire a second
Go. `agentTargetTaken` now covers both acting and done, so a replay of a
target this page took a lease on is ignored, and the late-mount watch
stops on either. Contract pins for the guard and the watch.

Written with AI assistance (Claude).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
a3bb21cbde Address review: one Go per tab, declines for a granted miss, and grace per overlay
Four review threads on the agent-target protocol and the hook stand-down.

Overlay: a tab acting on one target is busy for every other target
(`agent_target_in_flight`), so two held generate requests can never both
be claimed by one tab and the second Go can never overwrite the session
the first one minted. Every exit from actOnAgentTarget ends the acting
state, and teardown clears the target ledger, so a Go that never happened
does not refuse the next connection's targets. A miss after a granted
claim now declines (handing the lease back so another page or a remount
can serve) instead of posting a result that ended the request for every
tab.

Hook: the live-preview marker probe runs before the per-session edit cap,
so a file already past the cap stands down for a variants wrap instead
of emitting the suppression notice.

Server: each overlay's first no_match word extends the resolution grace
by the full window (its watch re-reports do not), so an overlay that
reports after another page's grace lapsed still gets its late-mount
watch instead of completing the roll call with a no_match verdict.

Tests: a Rust and a Node protocol case for the late overlay's grace, a
hook case for the cap-then-wrap order, and contract pins for the busy
check, the decline on a granted miss, and the teardown clear.

Written with AI assistance (Claude).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
79a27051a2 Address review: hold an all-no_match roll call open for a resolution grace
When the unresolvable page's decline was the last word, the roll call
completed on it, the answer said pending:false, and the page's watcher
never started, so an element that mounted a moment later was still
answered no_match. A page's no_match is a provisional word: the server now
keeps an all-no_match roll call open for IMPECCABLE_AGENT_TARGET_RESOLVE_GRACE_MS
(default 3000) after the first such report, re-judging when the grace
lapses, so a page that keeps watching can still claim (the stale report is
dropped on its eligible claim); a busy report still answers at once. The
overlay reports a miss immediately and re-checks every half second for as
long as the answer says pending. A genuine no_match now takes about the
grace instead of tens of milliseconds, inside the server's hold.

Rust integration case for the late mount claiming within the grace, the
protocol case, and the contract assertions updated; the contract documents
the grace and its env override.

AI-assisted: implemented and tested with Claude Code under maintainer
direction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
bd4ec3a11c Address review: the server ends the resolution watch, and a reconnect re-participates
Two ways a page's word could go stale after the resolve-before-claim
change: an element that mounts later than the quick re-checks, and an
EventSource reconnect that did not overlap the old connection (the server
drops that page's word on the close, replays the target, and the replay
guard ignored it, so the roll call waited on a word that never came).

A decline's answer now carries pending, like a denied claim does, so a page
that could not resolve the target reports the miss after the quick
re-checks (the roll call can complete on the other overlays' words) and
keeps re-checking once a second for as long as the server says the request
is pending, claiming the moment the element mounts; the server drops the
stale report on an eligible claim and ends the watch by answering
pending:false once the request resolved or timed out. The overlay tracks
its participation per target: a replayed target is ignored only while this
page is acting on it, and is otherwise handled again, so a busy or
unresolvable page re-declines (idempotent) and an idle page claims.

Unit, protocol, and contract cases updated; the decline answers now say
whether the request is still pending.

AI-assisted: implemented and tested with Claude Code under maintainer
direction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
46f1dd6b36 Address review: re-check a failed resolution before declining an agent target
A page's element can be momentarily absent (a route still rendering, an
HMR commit mid-swap), so a failed resolution is not that page's final
word. The overlay now re-checks at 300, 700, and 1500 ms, claims the moment
the element mounts (the server already drops the stale report on an
eligible claim), and reports only the last miss. A genuine no_match now
takes about two seconds instead of tens of milliseconds, well inside the
server's hold.

Also normalizes a path separator in the new hook unit test, which failed
on rust-windows because the audit's file path carries backslashes there.

AI-assisted: implemented and tested with Claude Code under maintainer
direction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
25263adc51 Roll call: a page that cannot resolve the target declines instead of claiming
Field-testing with two pages open showed the first-wins claim letting the
wrong page answer: a tab whose page lacks the element won the claim,
resolved the selector locally, and replied no_match while another page had
the element. The overlay now resolves the selector before any claim and,
when its page cannot resolve it, declines with reason no_match and the
resolution verdict; the same check runs on the busy-to-idle re-claim. The
server records that verdict on the report and, once every connected
overlay has declined, prefers a report that could serve later (a tab
mid-session or with an apply in flight, which answers busy so the agent
retries) over no_match, and returns the resolution verdict only when no
page can serve; the timeout uses the same precedence.

Also from the same field tests: a tab on another page of the app was
resuming this page's session from the per-origin localStorage cache after
a dev-server reload re-initialised it, then sat in GENERATING for a wrapper
it never renders and declined every later target. restoreSessionWithoutWrapper
now resumes a cached session only on the page that saved it, the check the
server-adoption branch beside it already applied.

Covered by two Rust integration cases, two protocol cases, and contract
assertions for the resolve-before-claim path and the page gate; the
cross-page scenario of the field harness passes on a two-page site.

AI-assisted: found by field tests and fixed with Claude Code under
maintainer direction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
da403a3410 Address review: the overlay, not the connection, is the roll-call participant
An EventSource reconnect opens a replacement connection under the same
page-level clientId before the old connection is seen to close, so the
close handler used to retire the reconnected overlay's report and hand
its lease back mid-flight. remove_sse_client now retires a client's word
only when no other connection still carries its id, the roll call counts
distinct overlays (plus id-less connections) instead of raw connections,
and the overlay ignores a replayed target it already handled, so a
reconnect never starts a second claim or a second Go. Covered by two new
HTTP cases in crates/cli/tests/agent_target.rs, a protocol case in
tests/live-agent-target.test.mjs, and the overlay contract suite.

AI-assisted: implemented and tested with Claude Code under maintainer
direction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
d397140a77 Port /impeccable generate to the engine crates
The Node-era server, CLI, hook, and pin halves of the generate command move
into the Rust workspace, with the protocol unchanged:

- crates/live: POST /agent-target is held open on a channel plus a timer
  thread (the manual-apply deferred pattern), releasing its turnstile
  ticket before it parks like /poll; /agent-target-result resolves it;
  /agent-target-claim is the roll call with its renewable lease. SSE
  connections carry the overlay's clientId: a late overlay is replayed
  every pending target, and a disconnect retires that overlay's report,
  releases its lease, and re-judges each roll call. Shutdown drains held
  requests with server_stopping.
- crates/live/src/live_generate.rs: the live-generate verb (the router
  already forwards every live* verb), same flags, verdicts, and
  _instructions, spelled with the engine's self command.
- crates/hook: every entry stands down on live preview markers
  (skipped: live-preview), checking the proposed content and the file on
  disk for hook-before-edit.
- crates/context: pin accepts generate; the crate's command-metadata.json
  copy carries its entry.

Tests: crates/cli/tests/agent_target.rs (six HTTP cases with an SSE reader),
tests/live-agent-target.test.mjs rewritten to drive the binary (28 cases,
registered in the live suite), hook stand-down cases, oracle goldens for
live-generate plus the re-recorded pin list goldens, the e2e prompt
assertion waiting for the journaled event, and the contract documented in
docs/CLI-CONTRACT.md.

AI-assisted: implemented and tested with Claude Code under maintainer
direction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
fc89b0ed62 Add /impeccable generate: agent-initiated live variants (Node-era squash)
Squash of the ten commits reviewed on PR #626, plus the last review
round's connection-aware roll call, before the rebase onto the Rust
engine: the generate command reference and router row, the overlay's
agent-target handling (roll call, leases, replay, rescue), the Node-era
live-server routes and live-generate CLI, the hook stand-down, the pricing
cards e2e fixture, and the unit, contract, e2e, and skill-behavior tests.
The server, CLI, hook, and pin halves are ported to the engine crates in
the commits that follow.

AI-assisted: implemented and tested with Claude Code under maintainer
direction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-15 05:45:49 +05:00
cb56ed6c19 Fix: detect placeholder contrast (#790) (#799)
* Fix: detect placeholder contrast (#790)

`detect` never read `::placeholder` color, so pale placeholders passed. Score them with the same WCAG math as body text, without host class/clip heuristics.

Prepared with AI assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix: match descendant ::placeholder hosts (#790)

`.form ::placeholder` kept the ancestor as the host. Reuse the hover combinator star-fill so the color lands on the inputs inside.

Prepared with AI assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix: placeholder-shown and gradient alpha (#790)

Browser scans skip when :placeholder-shown is false, so a live filled field does not keep the HTML value attribute's empty state. Translucent placeholders flatten over each gradient stop before scoring.

Prepared with AI assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix: trailing combinator only for ::placeholder hosts (#790)

`star_empty_compounds` turned `.label + ::placeholder` into `.label *+*`. Fill only a trailing empty compound so adjacent-sibling hosts still match.

Prepared with AI assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 13:49:59 -07:00
0c09f4c7e2 Fix: verify touch gestures in adapt, audit, and harden (#805) (#807)
* Fix: verify touch gestures in adapt, audit, and harden (#805)

The verification sections of adapt.md, audit.md, and harden.md listed
environments and layout properties but never had the agent exercise a
control's primary gesture, so an emulated viewport plus screenshots
could pass as touch testing. adapt now exercises the primary gesture
and the scroll-across trade and reports what produced the evidence,
audit checks broken touch interaction with code-level tells, harden
covers interrupted gestures and recovery, and a reference-contract
test pins the three sections.

Prepared with AI assistance (Claude Code), directed by @abdulwahabone.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Pin the scroll, exercise, and cleanup sentences in the reference test (#805)

Greptile flagged that the contract test pinned the new labels but not
adapt's scroll-across trade, audit's instruction to exercise the
gesture, or harden's drag-state and capture cleanup.

Prepared with AI assistance (Claude Code), directed by @abdulwahabone.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-10 13:45:37 -07:00
7a7579858c test: scenario 19 documentation case when the context launcher is denied (#791)
* Add degraded Setup path: must-read pack when the context launcher is refused

When the host denies the impeccable context launcher (issue #789, measured
in #744), the Setup fallback now names the degraded path and its
unconditional must-read pack: the routed command's reference and
craft-floor.md before any UI edit, and document.md before writing DESIGN.md.
init.md gains the degraded Step 1 behavior, docs/CLI-CONTRACT.md documents
the degraded contract, and scenario 19 gains a denied-launcher documentation
case asserting document.md and source reads precede the DESIGN.md write.

No version bump, no changelog entry, no generated harness sync.

AI was used for assistance.
Includes AI_PR_NOTICE.txt per the repository's contribution policy: this
change was prepared without maintainer approval on issue #789, so no PR is
opened by the agent.

Co-authored-by: Matt Van Horn <mvanhorn@users.noreply.github.com>

* Drop restated degraded-setup prose; keep the scenario 19 documentation case

The launcher-unavailable path already lives on main. This removes the
notice file and the restated SKILL, init, and CLI-contract text, and keeps
the denied-launcher documentation coverage. The notice must now land before
the first tool call after the denial, not only before the eventual write.

AI was used for assistance.

Co-authored-by: Matt Van Horn <mvanhorn@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-09-10 13:44:25 -07:00
67d018fe05 Fix: print JSON on live-poll --reply success (#800)
Successful --reply was exit 0 with empty stdout, so agents could not tell delivery from a hang. Prepared with AI assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 08:47:39 +05:00
3bdb9ff06c Fix: drop stale carbonize diagnostic on complete (#801)
Complete and discarded snapshots no longer keep carbonize_cleanup_required after cleanup is done.

AI assistance: Cursor Grok 4.6.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 08:47:16 +05:00
Paul BakausandGitHub a8ce5962d3 Fix silent launcher setup failures (#788)
* Fix silent launcher setup failures

Report cache creation, cache write, and download failures with recovery guidance while preserving lazy engine downloads.

AI-assisted by Codex.

* Fix Windows staging-write failure handling

Branch directly on redirection failure and reject staging directories before cleanup. Add coverage for an existing read-only staging file.

AI-assisted by Codex.
2026-09-08 19:20:16 -07:00
Paul BakausandGitHub 05ef444b7d Use GPT Image 2.5 Flare and native transparent assets (#786)
* Use GPT Image 2.5 Flare for image generation

Add explicit model selection and cover generation/edit request provenance. Compare 24 comp samples with Image 2 and Sunburst, documenting efficiency gains and the photo-preservation tradeoff.

Prepared with AI assistance under maintainer instructions.

* Validate native image transparency for asset production

Add repeatable cutout, glass, and reference-plate experiments with alpha measurements and compositing evidence. Record fidelity limits and the remaining asset-producer integration work.

Prepared with AI assistance under the maintainer instructions.

* Add native transparent PNG asset generation

Wire background options through generation and edits, preserve alpha, and update asset production to use supported commands with visual cutout checks.

Prepared with AI assistance under the maintainer's instructions.

* Fix native alpha review findings

Validate PNG paths for all background modes, score sparse and partial alpha over the sampled ground, and correct launcher and plate workflow guidance.

Prepared with AI assistance under the maintainer's instructions.

* Fix launcher paths in comp workflow commands

Quote existing launcher filenames for the target shell while retaining multiword command prefixes. Cover relative and absolute paths and actual shell argument parsing.

Prepared with AI assistance under the maintainer's instructions.

* Remove evaluation artifacts and docs from image PR

Keep the PR focused on runtime behavior, source skill instructions, and regression coverage. Preserve evaluation material locally.

Prepared with AI assistance under the maintainer's instructions.
2026-09-08 15:52:38 -07:00
Paul BakausandGitHub 6496f49a1e Fix skill workflow regression coverage (#783)
Clarify launcher fallback and completed documentation handoffs; separate bounded protocol checkpoints from opt-in browser-backed completion diagnostics. Correct fixture containment, target syntax, and artifact assertions. AI assistance: Codex, under maintainer direction.
2026-09-08 08:41:54 -07:00
Paul BakausandGitHub 44e825090e Set registered VS Code publisher identity (#777)
Use renaissance-geek for the Impeccable extension and guard the publisher identity in packaging tests.

AI assistance: Codex, under maintainer direction.
2026-09-07 12:25:27 -07:00
Paul BakausandGitHub 8b39f41949 Add native Cursor marketplace plugin packaging (#776)
* Add native Cursor marketplace plugin packaging

AI assistance: Codex, under maintainer direction.

* Document verified Cursor plugin installation and smoke tests

AI assistance: Codex, under maintainer direction.

* Fix Cursor plugin sync for license changes

AI assistance: Codex, under maintainer direction.
2026-09-07 11:50:15 -07:00
Paul BakausandGitHub 4957fd7069 Add declarative VS Code skill extension packaging (#775)
* Add declarative VS Code skill extension packaging

Stage a launcher-only Copilot skill bundle, validate relocation and VSIX packaging, and keep project-install hooks out of the extension.

AI assistance: Codex, under maintainer direction.

* Document verified Copilot extension smoke test

Record the successful read-only VS Code 1.136.1 run and the reload required after initial workspace trust. Keep minimum-version and remote smoke gaps explicit.

AI assistance: Codex, under maintainer direction.

* Fix VS Code packaging test version source

Derive the provider fixture version from the skill manifest and assert the packaged skill and extension versions agree.

AI assistance: Codex, under maintainer direction.
2026-09-07 11:40:31 -07:00
Paul BakausandGitHub 93bfe70c58 Fix truncated surface-brief slug collisions (#774)
* Fix truncated slug collisions

Prepared with AI assistance under maintainer-authorized automation.

* Preserve legacy long-slug reads

Prepared with AI assistance under maintainer-authorized automation.

* Harden legacy slug compatibility

Require target metadata before reading collision-prone legacy brief and critique paths. Add regressions for two long targets with the same pre-hash suffix.\n\nPrepared with AI assistance.

* Keep explicit access to legacy critiques

Allow identity-less pre-hash snapshots to be read by their exact legacy slug while keeping path and URL fallback identity-gated. Document the compatibility boundary and extend collision coverage.\n\nPrepared with AI assistance.
2026-09-07 11:40:05 -07:00
Paul BakausandGitHub 8426ac2f9a Clarify launcher-refusal fallback and correct behavior tests (#756)
* Clarify permitted work after launcher refusal

Correct behavior-test skill metadata and DeepSeek output limits; document the measured remaining Gemini warning-order failure and hook download trust boundary.

AI assistance: Codex, under pbakaus direction.

* Clarify behavior harness host-modeling scope

AI assistance: Codex, under pbakaus direction.

* Preserve planning-only scope after launcher refusal

Clarify applicable setup steps and cover denied-launcher planning. Retain the observed playbook-read failure under issue #744 rather than weakening its assertion.

AI assistance: Codex, under pbakaus direction.

* Test planning fallback warning order

Require an assistant warning after context launcher denial and before fallback context reads. Cover silent, late, and unrelated warnings with deterministic tests; retain the observed Sonnet omission under #744.

AI assistance: Codex, under pbakaus direction.
2026-09-06 20:34:28 -07:00
Paul BakausandGitHub 36e4cea693 Pin workflow actions and document live-edit script trust (#755)
Pin existing action versions to verified commit SHAs, restrict CI to a read-only repository token, and document the optional live-edit validation script. Preserve required sync/sheriff permissions and existing runtime behavior. Related to #480; extension permission assessment remains separate.

AI assistance: Codex, under maintainer direction.
2026-09-06 18:04:55 -07:00
Paul BakausandGitHub c4a4f035ed Fix: distinguish existing Stop findings from new debt (#754)
Use verified first-edit baselines to distinguish pre-existing text findings from new or unknown Stop findings. Preserve dirty worktrees, bound notice rendering, and keep explicit scans unchanged. Verified with the full Rust and Bun/Node suites and real Claude Code edit-to-Stop sessions. Related to #522; keep it open until an engine release ships the fix.

AI assistance: Codex, under maintainer direction.
2026-09-06 16:59:51 -07:00
6ebc24ad66 Add DeepSeek Harness as a supported skills provider (#746)
* Add DeepSeek Harness as a supported skills provider

npx impeccable install now detects ~/.dsh (or $DSH_HOME when it sits
under home) and installs into ~/.dsh/skills, the user-level skill root
DeepSeek Harness scans, with project-level .dsh/skills on the same
layout as other providers. Aliases: dsh, deepseek, deepseek-harness.

Engine: PROVIDER_DIRS / aliases / display / input order / global hint,
$DSH_HOME-aware user skills dir, provider id resolution from the skill
dir, pin harness dirs, bundle path normalization for hashing.

Build: dsh transformer target emitting the frontmatter DeepSeek Harness
reads (user-invocable, license, compatibility, metadata; unknown keys
are ignored there) with no emitHooks (DSH hooks are in-process plugins,
not on-disk manifests) and no agentFormat (no documented on-disk
subagent format); placeholders (AGENTS.md config file, ask_user_question
tool, / command prefix), provider block tags, universal README entry.

Docs: HARNESSES.md row and frontmatter column, CLI-CONTRACT constants,
README/DEVELOP/AGENTS provider lists.

Validation: cargo test --workspace; node scripts/run-tests.mjs core
(138 pass); bun run build (19 providers, dist/dsh artifact verified);
engine smoke against a fake HOME with a local bundle: install
--providers=dsh --scope=global, auto-detected install, and update all
resolve the .dsh provider.

Generated provider output intentionally omitted per repo policy; the
sync workflow regenerates tracked .dsh/skills after merge.

Prepared with AI assistance (DeepSeek Harness coding agent).

* Address review: DSH_HOME-only detection, generated-output pathspecs

- Detect DeepSeek Harness through the resolved $DSH_HOME (fallback
  ~/.dsh) instead of gating on a fixed ~/.dsh path, so a DSH_HOME-only
  setup is offered by a provider-less install; generalize the two
  env-relocated config-dir hints (OpenCode, DSH) into one shared probe.
- Add .dsh to the sync workflow's GENERATED_PATHS and CI's generated
  drift check so the tracked .dsh/skills payload is committed and
  validated.
- Cover both behaviors: new install_detection_tests (DSH_HOME-only,
  default ~/.dsh, refused outside-home override) and a CLI-CONTRACT
  note on the resolved detection path.

Validation: cargo test --workspace; node scripts/run-tests.mjs core
(138 pass); engine smoke: DSH_HOME-only fake HOME installs globally
into the resolved skills dir.

Prepared with AI assistance (DeepSeek Harness coding agent).

* Fix DeepSeek Harness home paths on Windows

Use native relative-path containment, cover case and drive boundaries, and verify relocated global install/update without changing project skills. Add DSH output coverage and correct the install documentation.

AI assistance: Codex, under pbakaus maintainer direction.

* Document the CLI limit on external DSH homes

Clarify that outside-home manual copies are not detected or updated by the CLI.

AI assistance: Codex, under pbakaus maintainer direction.

---------

Co-authored-by: Paul Bakaus <paul.bakaus@gmail.com>
2026-09-06 14:46:05 -07:00
Abdul WahabandGitHub 3e653377de Fix: Setup survives a refused launcher (#750)
Preserve Setup context and reference loading after launcher refusal, disclose the failure before editing, and limit Claude skill-directory substitution to SKILL.md. Add plugin-path and denied-launcher behavior regressions. Addresses part of #744 without closing its remaining scope.

AI assistance: Cursor on the original contribution; Codex on maintainer-directed follow-up fixes and validation.
2026-09-06 13:07:29 -07:00
Paul BakausandGitHub 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.
2026-09-06 12:44:15 -07:00
Paul BakausandGitHub bb7663ecfb Sign Windows engine releases with Azure Artifact Signing (#742)
* Sign Windows engine releases with Azure Artifact Signing

Isolate OIDC signing behind a maintainer-approved release environment and publish only verified, timestamped company-signed Windows output.

AI assistance: implemented and verified with Codex under maintainer direction.

* Clarify same-run artifact permissions

Keep least-privilege tokens: the pinned artifact actions use ACTIONS_RUNTIME_TOKEN for same-run transfers. Guard against opting into cross-run downloads and use role-based reviewer wording.

AI assistance: prepared and verified with Codex under maintainer direction.
2026-09-05 15:38:55 -07:00
044a04fd0d docs: add workflow guide for command entry points (#737)
* docs: add workflow guide for command entry points

* Refine workflow guidance into advice-only routing

Reuse the existing routing reference and docs map instead of shipping a parallel workflow catalog. Add reference-backed command comparisons, advice-only tests, and explicit-command precedence coverage.

AI-assisted maintainer revision prepared with Codex.

* Include routing guidance in behavior-test triggers

AI-assisted maintainer revision prepared with Codex.

* Constrain routing behavior tests to fixture-safe tools

Keep the real context loader but reject arbitrary host shell searches in the new advice scenarios. Preserve observable project writes and protect the staged skill; cover the restriction with offline regression tests.

AI-assisted maintainer revision prepared with Codex.

* Require actual reference reads in restricted routing tests

Do not count rejected shell reads as reference loading. Record the nine measured advice cases; explicit-command measurements remain pending the stricter retest.

AI-assisted maintainer revision prepared with Codex.

* Record measured workflow-routing baseline

All twelve focused cases pass across Claude Sonnet 5, GPT-5.6 Terra, and Gemini 3.7 Flash, including the stricter explicit-command retest.

AI-assisted verification prepared with Codex.

* Trim workflow routing guidance

Reduce added skill prose from 286 to 59 words while retaining the routing regression assertions. Record the missing-context reference-read flake and passing repeat.

AI assistance: prepared and verified with Codex under maintainer direction.

---------

Co-authored-by: Paul Bakaus <paul.bakaus@gmail.com>
2026-09-05 15:01:58 -07:00
Abdul WahabandGitHub a443ec0d8f Fix Claude non-interactive skill activation (#738)
Keep allowed-tools in shared source and suppress it only for Claude output and the plugin. Verified against current main with provider builds, local tests, real plugin loader checks, and an activation-only Claude A/B test reproducing the original denial and successful fixed activation. AI-assisted implementation and verification under maintainer direction.
2026-09-05 13:02:37 -07:00
Paul BakausandGitHub 6e61113e3c Fix: report removed launcher downloads accurately (#741)
* Fix: distinguish removed launcher downloads from checksum failures

Related to #740; keep the antivirus investigation open. Add executable launcher regressions and a native Windows CI lane. AI assistance: prepared with Codex under Paul Bakaus direction.

* Fix: diagnose downloads removed during cache placement

Cover removal and truncation around rename and preserve fail-closed behavior through cache placement. AI-assisted under maintainer direction.

* Test Windows launcher hash and placement failures

Inject failures at command boundaries in a staged test copy while retaining real launcher control flow. Cover both platforms with the same assertions. AI-assisted under maintainer direction.
2026-09-05 11:30:19 -07:00
Paul BakausandGitHub eebfb7c2ce Release: CLI 4.0.2 and engine 0.1.1
Ship signed skill-bundle verification, fix annotated-session checkpoint ordering, and pin all published engine platform packages. Validated with Rust, Node, browser, and provider-backed end-to-end tests. AI assistance: prepared and validated with Codex under Paul Bakaus direction.
2026-09-05 10:47:32 -07:00