1863 Commits
Author SHA1 Message Date
Paul BakausandGitHub f88b2837a7 Bump compatible Bun dependencies (#607)
Prepared with AI assistance from Codex under maintainer authorization.
2026-08-17 14:32:57 -07:00
Paul Bakaus cf8f295dc3 Simplify provider hook smoke orchestration
Centralize provider fixture, hook, and agent-launch contracts while preserving provider-specific verification behavior. Reuse the shared CLI argument parser and characterize the public usage contract.

AI-assisted: prepared by Codex under pbakaus's scheduled architecture-refactor authorization.
2026-08-17 08:45:12 -07:00
Paul Bakaus 665c51b903 Fix Windows hook migration dedupe
Normalize hook command separators before matching Impeccable-owned entries so updates replace legacy Windows guards instead of duplicating them.\n\nAI assistance: Codex implemented and validated this change under maintainer authorization.
2026-08-17 06:10:16 -07:00
github-actions[bot] 5c5553b1d7 Sync generated provider output 2026-08-16 21:26:22 +00:00
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>
2026-08-16 14:26:02 -07:00
github-actions[bot] 3fcfa7eedf Sync generated provider output 2026-08-16 21:25:33 +00:00
Paul BakausandGitHub 5050b66dbd Simplify hook manifest builders (#596)
Centralize the shared Claude-compatible PostToolUse and Stop schema while preserving every provider-specific matcher, path, notice, and timeout.\n\nAI assistance: Codex prepared this behavior-preserving refactor under pbakaus's scheduled architecture-simplification authorization.
2026-08-16 14:25:06 -07:00
Paul BakausandGitHub 21ad321a97 Centralize surface route normalization (#600)
Consolidate explicit and inferred surface route canonicalization behind one private rule, with characterization coverage for equivalent and invalid inputs.\n\nAI assistance: OpenAI Codex prepared this change under pbakaus's scheduled architecture-simplification authorization.
2026-08-16 14:24:31 -07:00
github-actions[bot] 9ce0350054 Sync generated provider output 2026-08-16 08:52:46 +00:00
Abdul WahabandGitHub f1560cc238 Merge pull request #590 from pbakaus/fix/comp-ground-sampling
Fix uncaught ground-color drift on comp-led builds
2026-08-16 13:52:16 +05:00
Abdul WahabandCursor e9c62278c1 Make the code-led GROUND fallback deterministic, compare like for like
The quality bar leaves the color-authority chain (it arrives as card
image paths and never governs composition). With no comp, a color
OWN-WORLD names is the target; when it names none, the review states
there is no GROUND authority instead of inventing a target. The build
side of the numeric comparison now samples the same way each record
was taken: patch average against patch average, gradient ends against
gradient ends.

AI-assisted change (Cursor), prepared under maintainer direction.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-16 13:39:57 +05:00
Abdul WahabandCursor 79c648a9ab Resolve bot review: code-led GROUND authority, sampling rules, tolerance
GROUND no longer lapses silently on code-led builds: with no comp to
sample, the authority is the colors OWN-WORLD and the quality bar name,
and no invented target beyond them. Non-uniform fields get sampling
rules (interior pixel, patch average for texture, both ends of a
gradient, never an edge), and the numeric comparison gets tolerance
semantics so render noise never fails a faithful build. The hunt hint
names the dark-ground prior beside the light one.

AI-assisted change (Cursor), prepared under maintainer direction.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-16 12:50:46 +05:00
36457e191f Bump skill-behavior test lineup: gemini-3.7-flash (#598)
* Bump skill-behavior google lineup to gemini-3.7-flash

gemini-3.7-flash replaces gemini-3.6-flash in DEFAULT_MODELS. The
README notes that the recorded gemini baseline cells were measured on
3.6-flash (or 3.5-flash where marked) and count as unmeasured on 3.7
per the suite's own cross-version rule, to be re-run on the next Setup
or routing change.

AI-assisted change.

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

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-08-15 19:03:18 -07:00
github-actions[bot] 7b646bafd6 Sync generated provider output 2026-08-14 23:15:40 +00:00
886cd669ef Fix: compile closest() selectors once per document in the static scanner (#575) (#595)
StaticElement.closest() handed the raw selector string to css-select's
is() on every ancestor step, recompiling the same selector N times for
an element N levels deep. StaticDocument now caches one compiled
matcher per selector (failed compiles cached as rethrowers so bad
selectors still return null). Findings are byte-identical across the
fixture corpus; scan time drops to ~62% on the fixtures and ~7x faster
on deep-DOM pages.

Prepared with AI assistance (Cursor agent), directed by @abdulwahabone.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 16:15:01 -07:00
Abdul WahabandCursor e975bec412 Harden monorepo design-root recognition and the home-directory stop (#570)
Read all four workspace-glob sources context.mjs reads (.impeccable
projectRoots, package.json workspaces, lerna packages, pnpm packages),
so lerna-glob roots and impeccable projectRoots no longer hit the same
abstention. Compare the walk against both the logical and realpath
forms of the home directory: on distros that symlink home paths
(/home to /var/home) the string comparison never matched, and the
post-boundary walk could inherit a workspace-declaring home's
DESIGN.md.

Written with AI assistance (Cursor); reviewed by maintainer.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-15 01:07:52 +05:00
Abdul WahabandCursor 91f2c7b47e Fix: strip inline YAML comments when reading pnpm workspace globs (#570)
An inline comment on a pnpm-workspace.yaml packages line defeated the
end-anchored flow-list regex and the block-list state switch, so
workspaces outside apps/ or packages/ went unrecognized. Reuses the
engine's existing stripInlineYamlComment, matching context.mjs.

Written with AI assistance (Cursor); reviewed by maintainer.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 23:47:59 +05:00
Abdul WahabandCursor dca8f1ca6f Fix: inherit the monorepo root's DESIGN.md in detect design-system rules (#570)
findDesignRoot stopped at the first package.json boundary, so every
design-system rule silently abstained for files inside monorepo
workspaces. The walk now continues past a workspace boundary to the
monorepo root that owns it, recognized the same way context.mjs does
(declared workspace globs, or a marker file beside apps/ or packages/
children). A nested repo with its own .git, a workspace-owned
DESIGN.md, and non-monorepo projects keep their existing behavior.

Written with AI assistance (Cursor); reviewed by maintainer.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 23:41:29 +05:00
github-actions[bot] c88d815e05 Sync generated provider output 2026-08-14 17:51:40 +00:00
3b87935958 Fix: keep raster provenance through the finish-review fix loop (#588)
* Fix: keep raster provenance through the finish-review fix loop

Three runs (two harnesses) showed the parent generating production
rasters after the producer returned: no exact embedded prompt, no
inventory row, orphan files. The asset contract in visualize.md was
phase-scoped to the build while new-work.md's fix loop licensed
"produce the named assets" with no rules attached.

- visualize.md: name the provenance contract, require the exact tool
  payload, and scope it to the run, fix rounds and rebuilds included.
- new-work.md: bind fix/rebuild rasters to the contract, add an
  embed-prompt --scan step before the verdict round, and extend the
  FINISH line to carry the condition through long builds.
- embed-prompt.mjs: add --scan mode listing rasters missing a prompt
  (exit 3 when any), reusing the existing read path.

AI-assisted change, prepared with Cursor under maintainer direction.

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

* Add cursor-control-8 comp vs final screenshots for PR evidence

AI-assisted change (Cursor), prepared under maintainer direction.

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

* Add cursor-control-9 comp vs final screenshots for PR evidence

AI-assisted change (Cursor), prepared under maintainer direction.

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

* Address review findings on the provenance gate

- Hoist the provenance rule out of the fix disposition into its own
  paragraph binding rebuild and fix alike, gated before either round's
  result goes back for review or verdict (Bugbot: rebuild skipped the
  scan when its fresh review shipped).
- A scan-flagged raster gets the record it is missing embedded, exact
  prompt for produced, origin for sourced/stock/pre-existing; deletion
  is reserved for abandoned rasters, never scan hits (Bugbot: gate hit
  non-generated assets on extensions).
- Document the scan command with its required directory argument
  (Greptile: literal command exited before scanning).
- Align the FINISH line on the provenance token.

AI-assisted change (Cursor), prepared under maintainer direction.

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

* Remove evidence images from the diff; they live on the pr-evidence branch

AI-assisted change (Cursor), prepared under maintainer direction.

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

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 13:51:03 -04:00
Abdul WahabandCursor d40274c47d Remove evidence images from the diff; they live on the pr-evidence branch
AI-assisted change (Cursor), prepared under maintainer direction.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 22:14:18 +05:00
Abdul WahabandCursor 9213bf1511 Generalize color sampling beyond the cream-ground case
Accents join the sampled record alongside ground and dominant fields,
every recorded color (not only the ground) is compared by number
during the build, and the light-ground-only rationale clauses become
value-neutral so dark and saturated comps get the same protection.
Rule anchor renamed to skill-color-by-number to match its scope.

AI-assisted change (Cursor), prepared under maintainer direction.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 21:26:08 +05:00
Abdul WahabandCursor 8230426df8 Add PR evidence images (cursor-control-8 ground drift)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 20:27:19 +05:00
Abdul WahabandCursor 5b7c9e93cb Fix uncaught ground-color drift on comp-led builds
Sample the approved comp's ground and dominant-field hexes into the
brief (visualize.md), judge the built page's ground by number against
that record including the net value under textures (new-work.md), and
make GROUND a mandatory fidelity-matrix row beside TYPE and MATERIAL
(finish reviewer). Pre-comp palette chips are retired at approval.

AI-assisted change (Cursor), prepared under maintainer direction.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 20:27:19 +05:00
Paul Bakaus 5a149f3fdb Release: skill v4.1.1
Assisted-by: Claude Code
skill-v4.1.1
2026-08-14 08:58:23 -04:00
github-actions[bot] 663162cf10 Sync generated provider output 2026-08-14 12:46:12 +00:00
Paul BakausandGitHub a98ee8b10e Simplify local detector dispatch (#577)
* Simplify local detector dispatch

Centralize HTML-versus-text file routing for stdin, directory, and direct-file scans. Add CLI characterization coverage for both stdin paths.

Prepared with AI assistance under maintainer pbakaus's standing scheduled-refactor authorization.

* Strengthen detector dispatch characterization

Put the HTML-only finding in a linked stylesheet so the text engine cannot satisfy the static-engine assertion.

Prepared with AI assistance under maintainer pbakaus's standing scheduled-refactor authorization.
2026-08-14 05:45:42 -07:00
github-actions[bot] c7588067a4 Sync generated provider output 2026-08-14 12:44:59 +00:00
Paul BakausandGitHub 49d8cbff16 Comp-fidelity review discipline + conciseness pass on core references (#586)
* Comp-fidelity review discipline + conciseness pass on core references

Process fixes derived from a real Codex session (Hanasaku landing page)
where a build drifted wholesale from the approved comp and still shipped
under a reviewer pass:

- finish reviewer: new Evidence check (check 0) with a fourth
  disposition, recapture, for malformed screenshots; a review on invalid
  evidence binds nothing and owes a full re-review, not a verdict pass
- finish reviewer: verdict passes exit scoring mode when recaptures fail
  check 0 or when the packet carries user-supplied screenshots that
  contradict a prior verdict (those force a fresh full review); a ship
  earned in a verdict pass covers the scored fixes, not the whole surface
- new-work: capture-validity rules (settle entrance motion, capture from
  document top, comp comparison at comp dimensions, open every file once
  before sending); user's actual viewport joins the inspected sizes
- new-work: hero checkpoint now writes .impeccable/review/hero-repro.png
  and the reviewer verifies it exists under Persistence
- new-work: comp authority is explicit (only the user can downgrade it);
  handoff reports the verdict at its actual scope; user evidence reopens
  a full review; documenter re-runs when fixes land after documentation
- craft-floor: Refuse entry for geometric masks approximating organic
  photographic contours (the circular-cutout failure)
- editorial conciseness pass over new-work.md, visualize.md, and both
  agent files: tighter sentences, no dropped rules, all rule markers and
  mechanical tokens preserved

Assisted-by: Claude Code

* fix: define the ship disposition in new-work's action paragraph

Copilot review finding: the paragraph claimed exactly four disposition
words but defined only recapture, rebuild, and fix.

Assisted-by: Claude Code

* fix: rebuild returns get a full review; recapture return shape in preamble

Cursor Bugbot findings:
- a return following a rebuild directive is now a fresh full review on
  both sides of the contract, never a verdict pass, so a wholesale
  rebuild cannot earn a scoped ship on the directive alone
- the turn-ceiling preamble now names the recapture return shape instead
  of contradicting it with "the five sections"

Assisted-by: Claude Code

* fix: absent required captures fail the evidence check

Greptile finding: a packet with no desktop.png/mobile.png (or missing
native device-class captures) routed to the missing-input notice and
could still reach ship. A required capture that is absent now fails
check 0 exactly like a malformed one and forces recapture; the
missing-input allowance in the preamble excludes captures.

Assisted-by: Claude Code

* fix: user-viewport capture is a required, named input to the review

Greptile finding: the evidence gate hard-coded web requirements to
desktop.png and mobile.png, so a reported user viewport could join the
inspected set and still ship uncaptured. The parent now saves it as
user-<width>.png and names every inspected viewport required in the
packet; check 0's required set includes every brief-named capture.

Assisted-by: Claude Code
2026-08-14 05:44:26 -07:00
c8f476b330 Take every themed list in an entry, not the first one (#585)
* Take every themed list in an entry, not the first one

A long changelog entry is grouped into themed lists behind cf-group labels, and
the extractor stopped at the first one. skill-v4.0.0 shipped 6 of its 19
bullets that way, and v4.1.0 would have shipped 6 of 21.

This is the same shape as the bounded-search fix one commit earlier: the
extractor treated "found a list" as "found the notes". It now collects every
cf-items list inside the entry's own article and joins them, so grouping an
entry for readability cannot silently truncate its release notes.

Written with AI assistance (Claude Code).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Name the malformed case separately

An entry that opens a cf-items list and never closes it inside its article
matched nothing, and the failure said the entry had no list of its own. That is
a different repair, and the message sent you looking for the wrong thing.

Written with AI assistance (Claude Code).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 01:28:36 -04:00
2c33196c51 Release: skill v4.1.0, CLI v3.6.0, extension v1.3.2 (#584)
* Release: skill v4.1.0, CLI v3.6.0, extension v1.3.2

Skill 4.1.0: the build path becomes a recorded setting with a per-round
toggle, the direction round routes challengers by verdict, surface rounds deal
structure, and critique delivers its report and its close.

CLI 3.6.0: contrast findings stop assuming white when the ground cannot be
read, waivers scope to the element that carries them, and Hermes Agent and
Antigravity install natively.

Extension 1.3.2: no source change, but the bundled engine is rebuilt at
release, so the same 59 rules ship with the false-positive work behind them.
Chrome and Firefox from the one manifest.

Harness output regenerated with build:release, which is what the version
validator checks against the manifests.

Written with AI assistance (Claude Code).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Bound release-note extraction to the entry it names

Every v4.0.x skill release shipped v4.0.0's notes. The extractor took the
first `<ul class="cf-items">` after the version header with no upper bound, and
the v4.0.1 through v4.0.4 entries wrote their bullets in a `cf-entry-list`
instead, so the search ran past all four and landed in v4.0.0. Nothing failed,
because finding a list somewhere was treated as success.

The search now stops at the entry's own `</article>` and fails with the reason
when the entry has no readable list, which is the case the old code silently
published its way through. The changelog side is fixed in impeccable-site,
where those five entries now use `cf-items` like the other 46: `cf-entry-list`
also had no CSS at all, so their bullets were rendering unstyled on the
changelog page.

Written with AI assistance (Claude Code).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
ext-v1.3.2 cli-v3.6.0 skill-v4.1.0
2026-08-14 00:31:18 -04:00
github-actions[bot] 103343d746 Sync generated provider output 2026-08-14 04:12:14 +00:00
Paul BakausandGitHub 19ed691810 Merge pull request #583 from pbakaus/fix/build-path-consent-in-init
Ask the build path as its own question, and record only a real answer
2026-08-14 00:11:50 -04:00
Paul BakausandGitHub 43f418a565 Merge pull request #582 from pbakaus/fix/build-path-flip-inspiration-stack
Flipping to comp demotes the inspiration instead of stacking a second slot
2026-08-14 00:11:29 -04:00
Paul BakausandClaude Opus 5 ae56d719af Name the image-gen sources and the unanswered default
Two review findings on #583.

"context.mjs reports it" undersold what counts: that directive only fires on an
OPENAI_API_KEY, so a harness with a native image tool and no key generates
images while the boot output says nothing. Read literally, the step would skip
the question exactly where the toggle belongs. It now names both sources and
says a silent boot is not evidence.

The unanswered branch said to state which path the session takes without saying
which one it is. Left implicit, an agent picks its own, which is the failure
this step exists to stop. It now names comp-first, the default new-work applies
when nothing is recorded.

Written with AI assistance (Claude Code).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 00:06:17 -04:00
Paul BakausandClaude Opus 5 1f0169d126 Clear the render clock when a poll generation goes stale
Follow-up to the generation stamp: both probe callbacks returned on a stale
generation without clearing the elapsed-time interval. tryLoad clears it on
re-entry, but a probe that finishes stale schedules no re-entry, so flipping
back while one was in flight left the interval ticking for the rest of the
page's life. Both exits now clear it, which is what the isConnected guard above
them already did.

Written with AI assistance (Claude Code).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 00:01:21 -04:00
Paul BakausandClaude Opus 5 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>
2026-08-13 23:56:51 -04:00
github-actions[bot] 2935d89512 Sync generated provider output 2026-08-14 03:45:40 +00:00
Paul BakausandGitHub 940b27246d Merge pull request #581 from pbakaus/fix/issue-580-svelte-variant-mount
Stop emitting a JSDoc cast into every Svelte variant (fixes #580)
2026-08-13 23:45:05 -04:00
Paul BakausandClaude Opus 5 83e8b4645c Ask the build path as its own question, and record only a real answer
A live Codex session folded the build path into the stack question as a
trailing recommendation ("I recommend static and code-first"), never said what
either name means, treated its own recommendation as the user's answer, and
wrote `buildPath: code` as a standing default. The user disagreed and flipped
the board to comp-first, but a flip binds one session, so the unasked default
stayed on disk to steer every later round.

Step 5 said to "ask once ... stated as the trade it is", which the run
violated, but the step left the shortcut open: it sits after the interview
ends, it never says a recommendation is not an answer, and unlike the stack
question it offers no way to end without a value, so an agent holding no answer
writes one anyway.

Now: it is its own question, never a clause inside another; the trade is stated
in the question the user reads, because the two names mean nothing on first
contact; only the user's own choice is written; and an unanswered question
records nothing and says so. Unset is a working state, since the page toggle
governs the session and new-work's one-time offer still captures the answer at
the first flip.

The same run also wrote "Code-first build path" into PRODUCT.md's `## Stack`,
so the step now says the config is the only place this lives: a copy in product
truth outlives the setting and steers rounds nobody can trace back to it.

Written with AI assistance (Claude Code).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 23:44:38 -04:00
Paul BakausandClaude Opus 5 b7960ecde3 Keep the scaffolder test inside its own workspace
Two review findings on #581, both fair.

The scratch app symlinked the whole of the repo's node_modules, so the
scaffolder's output directory, `node_modules/.impeccable-live`, resolved to the
REPO's copy. Variants were written there and survived `afterEach`, which only
removed the temp dir; the next case reused the session id, and the scaffolder
keeps existing variant files, so a case could parse a previous case's source
against a fresh manifest. Now only `svelte` is linked, into a node_modules the
workspace owns, and each case gets its own session id. Svelte's own
dependencies still resolve, because node follows the link to its real path
before looking for them.

The comment also pointed at a `PROPS_SCRIPT_SHAPES` symbol that does not exist
in the test file. Dropped the name and kept the file reference.

Written with AI assistance (Claude Code).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 23:38:35 -04:00
Paul BakausandClaude Opus 5 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>
2026-08-13 23:32:02 -04:00
Paul BakausandClaude Opus 5 5961269cb5 Stop emitting a JSDoc cast into every Svelte variant (fixes #580)
Live mode scaffolds each Svelte variant with a props script that annotated the
declaration:

    /** @type {{ title: string; }} */
    let { title } = $props();

A JSDoc `@type` written directly before a value is also JSDoc's cast syntax,
and esrap 2.3.3, the printer Svelte emits JS through, moves that annotation
onto the template's own declaration:

    var /** @type {{ title: string; }} */ (h1) = root();

`var (h1) = ...` does not parse. The .svelte source is valid, the compile
succeeds, and the failure lands in the browser's dynamic import as "Unexpected
token '('": the variant never mounts and the session shows nothing. `@typedef`
carries the same shape without being a cast, so both builders emit that.

This is not test-only. Every Svelte variant we generate carried the construct,
so live mode was broken for any user whose install resolved esrap 2.3.3.
Svelte declares `esrap: ^2.2.12`, so a fresh install takes it; this repo's
lockfile pins 2.3.0, which is why unit tests stayed green while the fixture,
which installs into a temp dir, did not.

Two reasons the existing pre-publish guard could not have caught it, now
recorded next to it:

  - `compileCheckVariants` compiles with `generate: false`, so there is no
    emitted JS to inspect.
  - `loadSvelteCompiler` resolves the compiler through createRequire, which
    Svelte's export map routes to a prebuilt CJS build. A dev server imports
    `src/compiler`, and only that path runs the app's installed printer. The
    guard was checking a different compiler than the browser runs.

The new suite therefore imports the compiler as ESM and asserts the emitted
JavaScript parses, rather than pinning the comment style: a future printer that
mangles some other construct fails it too. The first draft used createRequire
and reported green against the exact input that breaks in a browser, which is
the mistake worth not repeating.

Verified against svelte 5.56.9 with esrap 2.3.3. Full live-e2e sweep green,
26 fixtures.

Written with AI assistance (Claude Code).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 23:18:29 -04:00
github-actions[bot] 4f08ff3bfc Sync generated provider output 2026-08-14 03:00:43 +00:00
Paul BakausandGitHub 9d723f39df Merge pull request #579 from pbakaus/fix/build-path-config-migration
Build path becomes a config key existing projects can actually reach
2026-08-13 23:00:09 -04:00
Paul BakausandClaude Opus 5 d6c2442dbe Say why the flip is session-only, not just that it is
The BUILD_PATH_DEFAULT line ended on a bare absolute: a flip "is never written
back to the config". True wherever the line appears, since it is emitted only
when a default is already recorded, but the sentence does not carry its own
scope and has now been read twice as a rule that overrides new-work's one-time
offer. That is the same failure the previous commit fixed in serve-question,
where an unscoped "never write it" did override the offer.

The directive now states the condition it depends on and names where the
exception lives, so a reader who meets the line without the surrounding code
cannot draw the wrong rule from it.

Written with AI assistance (Claude Code).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 22:52:44 -04:00
Paul BakausandClaude Opus 5 816ffe92d0 Surface the build-path finding in doctor, and keep cwd out of the lookup
Round two of review findings, all four valid.

`doctor` builds its own finding list and never called `checkBuildPathUnset`,
so `config-build-path-unset` could not appear in the report even though
doctor.md documents it. That is also the only path left once stalenessCheck is
off, which is exactly when someone is looking for it.

The lookup chain included `process.cwd()`, which lets an ambient invoking
directory decide another project's workflow: run from workspace A with
--target resolving onto workspace B, and B inherited A's buildPath ahead of
the repository default. The chain is now the resolved project then the repo
root, matching `checkBuildPathUnset` exactly; cwd stands in only when no
project resolved at all.

Two prose contradictions, both mine. new-work said to write the value "when
the user says yes" and then to "record the answer either way", which reads as
persist-on-yes-only and leaves the decline to be asked again next session. It
now says the write always happens and the answer picks the value. The README
still pointed existing projects at re-running init, which is the problem this
PR exists to solve; it now names the toggle as the migration path.

The workspace-isolation test earned a correction of its own: the first version
passed a relative --target, which resolves against the caller's cwd and puts
projectRoot back on the calling workspace, so it asserted nothing.

Written with AI assistance (Claude Code).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 22:41:32 -04:00
Paul BakausandClaude Opus 5 c0e7f2d778 Read the repo-root build path, and stop overstating what a flip forbids
Two findings from Greptile on #579, both about the same key seen from
different roots.

`appendBuildPathDirective` searched projectRoot and cwd but never repoRoot,
while `checkBuildPathUnset` reads both. In a monorepo that committed the
preference once at the root, the two disagreed in the worst direction: the
staleness finding stayed silent because a value existed, and the directive
never named it, so nothing on screen explained why the recorded default was
not being honored. Roots are now ordered nearest first, workspace over repo
root, with regression tests for both the fallback and the override.

The ANSWER line for a flipped path said "never write it to settings". The
page indeed never writes it, but the sentence read as a rule and applied
itself to new-work's one-time offer, which exists for exactly the case a flip
creates: a project with no recorded default, asked once after the round
closes. It now states what the page does and names the exception.

The same report's first issue also named context.mjs, and that part does not
hold: its directive is emitted only when a value is already recorded, which is
precisely when session-only is the correct instruction. Left as is.

Written with AI assistance (Claude Code).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 22:20:19 -04:00
Paul BakausandClaude Opus 5 65de2d294b Raise the skill-behavior timeout that was grading haste over thoroughness
`initialized natural build` looked like a third defect on main: sonnet began
implementation before the attended concept checkpoint, three runs in a row.
It is flaky, not broken, and the measurement setup was the larger problem.

A run that stops to put the concept to the user before building takes about
579s on sonnet. A run that skips the checkpoint and fails the assertion
finishes in 130-200s. The suite capped each test at 300s, so the thorough path
was killed as a timeout and the hasty path was graded as a result: the cap was
selecting for the behavior the scenario exists to forbid. Raised to 900s, with
the reasoning recorded next to the number so it is not trimmed back as a
mystery constant.

The baseline is corrected accordingly: the scenario is flaky (1 of 4), not
failing, and readers are told to check a duration against the cap before
calling a slow failure a behavioral one.

Written with AI assistance (Claude Code).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 22:13:10 -04:00
Paul BakausandClaude Opus 5 07663f5fbd Stop the update directive from spelling out a command it forbids
Two defects the skill-behavior baseline had recorded as failing on main.

`UPDATE_AVAILABLE` told the agent to ask once, then said "If they agree, run
`npx impeccable update`", then said to continue without waiting. Nothing gated
the run on an answer, and the same sentence removed the wait that could have
produced one, so the command read as the next step and sonnet took it. The
offer stays; the command leaves the turn. Running it mid-session rewrites the
files the session is reading and only takes effect next session, so there is
nothing to gain by running it now, and the directive says that rather than
relying on the model to infer it. Failed 3 of 3 before, passes 3 of 3 after.

Scenario 15 was a broken fixture, not a routing defect. The iOS workspace held
PRODUCT.md and nothing else, so `audit the app in this workspace` named an app
that was not there: sonnet spent its step budget hunting for it, including a
`find /` across the filesystem, and read no reference file at all. The
assertion reported "loaded audit.md instead of the variant" when the truth was
"loaded neither". One SwiftUI screen makes the request answerable, and the
scenario then passes on unmodified main, which is the evidence that the skill
text was never at fault. This is the convention MINIMAL_LANDING_HTML already
established for the web scenarios; the native fixture never received it.

Written with AI assistance (Claude Code).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 22:04:53 -04:00