Commit Graph
293 Commits
Author SHA1 Message Date
Abdul WahabandGitHub 2b1804deaa Fix: ignore review and questions dirs in README gitignore (#677)
Fixes #669.\n\nAI-assisted change: reviewed and merged by Codex under maintainer direction.
2026-08-31 18:20:58 -04:00
Paul Bakaus 9434dde9af Merge main: skipScan visual-contrast coverage, live overlay waivers, generated output sync
The generated browser bundle is rebuilt from the merged engine sources in the next commit's build step (both branches had regenerated it).

AI-assisted (Claude Code).
2026-08-28 16:01:35 -07:00
152d6940b0 Fix: harden live overlay detector waivers (#639 follow-up)
Read waiver config from every live root (appRoot, contextRoot, repoRoot),
so monorepo projects whose config lives at the repo root reach the
overlay; serialize served roots and page identities repo-relative there.

Resolve each page URL to its actual serving file via the inject config's
resolved page list before applying file-scoped waivers; ambiguous URLs
keep the conservative common-ancestor fallback (PR #645 review
discussion r3840011436).

Honour detector.ignoreFiles: a wholly waived page now scans to zero
findings in the overlay, matching the CLI and the edit hook.

Guard the resolver call so a throwing resolver degrades to an unfiltered
scan instead of breaking the detect toggle.

Match design-system-color waivers by color value across hex and rgb()
spellings, and stop extracting font values for bounce-easing findings,
mirroring extractFindingIgnoreValue. Regenerate the browser bundle.

AI-assisted change: reviewed, planned, and implemented with Claude Code
under maintainer direction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 18:38:36 +05:00
Paul BakausandAbdul Wahab 3df4c4b10d Simplify CI test plan routing
Collapse the nightly alternate plan into the shared event routing while preserving every GitHub output and schedule behavior. Strengthen the nightly characterization for all deterministic suites.\n\nAI assistance: prepared by OpenAI Codex under maintainer pbakaus's standing scheduled-refactor authorization.
2026-08-28 15:00:56 +05:00
5330fa358e Fix: honour .impeccable detector ignores in the live overlay (#639)
The live overlay's detect scan ran unfiltered: requestDetectScan() posted
only { scanId }, so detector.ignoreRules and detector.ignoreValues in
.impeccable/config.json reached impeccable detect and the edit hook but
never the surface a designer actually watches.

The server now serializes the project's detector waivers into the /live.js
prelude (window.__IMPECCABLE_PROJECT_IGNORES__), read per request through
hook-lib's readConfig so config.local.json wins and edits land on the next
tab reload. A new script part, live-browser-ignores.js, resolves that
config against the page URL when a scan starts: ignoreRules suppress
outright, wildcard ignoreValues suppress their rule in the files their
globs name, and the remaining entries ride along as disabledValues for the
detector to match on each finding's own value. The detector bundle applies
those where the findings are assembled, since the overlay draws its own
markers from the collected findings.

Scope resolution mirrors cli/lib/impeccable-config.mjs deliberately: the
same glob dialect (globToRegex, including {a,b} alternation), the same
path-suffix matching as findingMatchesScopedIgnoreFile, and the same
refusal to apply an unscoped wildcard entry. The served-root prefixes that
bridge project-relative globs and site-relative URLs come from the inject
config's own files globs, never from the ignore globs; deriving them from
the ignore globs lets one entry scoped to prototype/library/** lend its
prefix to every page and suppress site-wide, which looks like success
because the numbers go down.

Known gaps, recorded in the detector comment: the motion value extractor
is not mirrored, so a value-scoped bounce-easing waiver only matches when
the finding carries ignoreValue directly, and design-system-color matches
on the normalized string without the CLI's color-equality fallback.

Tests: unit tests for the resolver part (stale globals, string ignoreRules,
malformed entries, directory URLs, percent-escapes, glob metacharacters,
the roots trap), an extension-mode puppeteer test that disabledValues
suppress exactly the waived findings, and the live-browser regression pin
now asserts the new scan config shape instead of { scanId }.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 07:13:43 +05:00
Paul BakausandAbdul Wahab 9ca77151de Register tests/hero-checks.test.mjs and lib/hero-checks in the suite map
AI-assisted (Claude Code).
2026-08-28 06:13:59 +05:00
Paul BakausandAbdul Wahab 99f73390db Comp fidelity: font ranking that holds without a browser, spec refuses painted chrome, control-box veto only for discrete controls
font-match / catalog index
- Index schema 2 adds a third render, 48c (48px cap, ALL CAPS text). Caps headline crops have no x-height band and ranked against mixed-case renders as barcode faces; they now route to the caps render.
- Non-text families (barcodes, redacted, flow, dingbats, effect faces) are excluded from candidates.
- The distance adds a gross width and weight gap (log ratio of advance and ink density) so a face 50% wider or 35% lighter cannot rank first on run-length detail alone; the index stores those readings.
- Multi-line all-caps crops vote on x-height across lines: one line's crossbars no longer give the crop a spurious x band.
- With no browser, --rank records the catalog's nearest face (source catalog, size estimated) so the spec gate can close; the NEXT line and new-work.md say not to install a browser or hand-write a choice.
- font-match stamps the choice it writes; the spec gate refuses a chosen face it did not write (sessions typed Arial Narrow into spec.json to pass).
- IMPECCABLE_NODE_MODULES lets a harness lend a playwright.

comp-spec
- A region note that describes painted material (diagram, drawing, photo, texture...) under a code kind is refused at the spec unless codeDrawn is set: the exploded carburetor filed as chrome is a plate about to be redrawn in SVG.

build-phase
- The control ink-box veto applies only when the comp's ink is a discrete element and the build's box is too; a full-width bar told one session six times that 1376x87 was 1382x102 with no edit able to move it.

AI-assisted (Claude Code).
2026-08-28 06:13:59 +05:00
95294e464a font-match v2: fingerprint the comp lettering and pick candidates from a Google Fonts catalog index
lib/font-fingerprint.mjs replaces the three-number fingerprint with
size-invariant shape features (x-height ratio, stroke contrast, stem width,
run-length quantiles, roundness, serif signal, width spread) and a
noise-normalized distance; family recall on a held-out self-test rose from
13% to 72% top-5. data/font-index.json carries the whole Google Fonts
catalog (3,092 faces at two cap sizes, 707 KB); font-match --rank fingerprints
the comp crop, takes the 25 nearest faces from the index (plus the model's
own names), renders them at the comp's cap height, ranks by the same
distance, and prints a proof sheet and the CSS to use. scripts/build-font-
index.mjs rebuilds the index at release time.

AI-assisted (Claude).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-28 06:13:59 +05:00
f5751c4d75 font-match: choose the face by metrics; chroma-keyed plates; ink-box report; verbatim words in the hero
font-match.mjs fingerprints a text region's lettering in the comp (cap
height, glyph advance per cap = width class, ink fraction = weight class,
tracking), renders candidate faces at that cap height in a headless
browser (yours plus a shortlist for the width class), and ranks them by
distance with a proof sheet; the spec gate refuses to close until the lead
text region is measured and ranked. generate-image --plate keys ink-on-
ground plates to alpha (chroma) so the drawing sits on the page's own
ground; the plates gate scores keyed plates composited over the region's
ground. comp-diff reports each region's ink box; the hero gate names a
control whose box height or width differs from the comp. The hero
instruction copies the comp's words verbatim; rewording is a stated
decision after the hero passes.

Driven by a human review of the r3 side-by-sides: face width and weight,
plate ground, control row height, and content substitution.

AI-assisted (Claude).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-28 06:13:59 +05:00
b0fc2e8801 Add comp-diff, comp-spec, and build-phase: measured comp fidelity for the build phase
Dependency-free PNG codec, perceptual metrics (structure / color / detail /
bands), side-by-side + heatmap + per-region crops, a measured spec from the
approved comp (grid overlay, sampled palette, plate list), and a phase state
machine whose spec / plates / hero gates run the diff instead of asking the
model to remember the image.

AI-assisted (Claude).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-28 06:09:46 +05:00
Abdul Wahab 043e8a5bfd Merge origin/main into fix/570-monorepo-design-root 2026-08-25 06:58:14 +05:00
Abdul WahabandCursor 35ae07339b Fix: parse Grok Build camelCase hook stdin (#646)
Grok was classified as GitHub Copilot, so the design hook skipped every
edit with no-file-path and never ran Stop. Normalize toolInput/sessionId
and treat Stop additionalContext as the Grok product.

Prepared with AI assistance.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-24 05:28:21 +05:00
Abdul WahabandGitHub c29f30fa34 Merge pull request #616 from pbakaus/codex/issue-614-remove-multiedit
Remove stale Claude MultiEdit matcher
2026-08-22 04:49:56 +05:00
Paul Bakaus 7d5c60d291 Remove stale Claude MultiEdit matcher
Claude Code now folds multi-edit behavior into Edit, so keep generated and repaired hook manifests aligned with the current Edit and Write tools. Grok keeps its compatibility matcher unchanged.

AI assistance was used to implement and validate this change.
2026-08-19 09:28:28 -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 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
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
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
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>
2026-08-14 00:31:18 -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
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 d4e1b0902f Enforce the ask_instruction sentence-initial contract
Review on #576 caught document.md:71 splicing {{ask_instruction}} after
"then", which is the same defect this branch set out to fix. Rendered for
Codex it produced "Show the user the existing file, then STOP and use Codex's
structured user-input/question tool...". The line now starts a new sentence.

The comment added to PROVIDER_PLACEHOLDERS asserted the contract without
enforcing it, which is exactly how four reference files shipped the splice in
the first place. validateAskInstructionSites() in build.js now checks every
call site and fails the build on a mid-sentence interpolation, and the comment
points at the gate instead of asking authors to remember.

Prepared with AI assistance (Claude Code).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 13:24:29 -04:00
Paul BakausandClaude Opus 5 0e5c6cbe17 Keep critique's report out of the question's message
The critique report and the AskUserQuestion call shipped in one assistant
message, so the report stayed hidden until the user answered the picker and
the command read as if it had never run.

Reorder critique's persistence steps so the temp-file cleanup runs after the
report and trend line are sent. That cleanup now ends the message carrying the
report, leaving the questions to open a fresh one. Both critique.md and
overdrive.md state the constraint and why it exists, so the ordering is not an
unexplained sequence a model can optimize away. Overdrive additionally moves
its direction descriptions inside the question options, where the user is
actually reading them.

Also fix the ask_instruction splices. The placeholder is a complete sentence,
but five call sites spliced it mid-sentence and shipped text like "stop and
STOP and call the AskUserQuestion tool to clarify. before expanding it". Every
call site is now sentence-initial and the twelve lowercase provider values are
capitalized to match, with a comment in utils.js pinning the contract.

Record a workflow-contract baseline for the current model lineup. The two
failures seen while validating this change are pre-existing: bolder refinement
fails on deepseek-v4-flash identically with bolder.md reverted to HEAD, and
redesign replaces DESIGN is flaky on assertions driven by new-work.md, which
this change does not touch.

Prepared with AI assistance (Claude Code).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 01:06:33 -04:00
Paul BakausandGitHub e36833ce21 Merge pull request #521 from digitallamb/pr/hermes-provider
Add Hermes Agent as a supported provider
2026-08-12 16:07:43 -04:00
045865918a Held for review: agent placeholder substitution, reviewer recapture contract, base-directory script form (#544)
* Resolve {{scripts_path}} in the agent bodies Codex ships

Three code paths emit an agent body: the degraded fallback reference, the
.toml nested inside the skill for Codex, and the native agent file. Only the
nested .toml skipped placeholder substitution and rule-marker stripping, so
the codex and .agents dists shipped `node {{scripts_path}}/embed-prompt.mjs`
verbatim in the asset producer, and every caller had to substitute the token
itself at load time.

All three now render through renderAgentBody(), and the new regression test
asserts a runnable embed-prompt command on each emitted surface plus a
synthetic agent proving markers and placeholders resolve in the nested .toml.

Prepared by an AI agent (Claude Code) under pbakaus's instruction.

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

* Give the finish reviewer's screenshots one fixed address

The Input Contract asked for "desktop and mobile screenshot paths captured by
the parent" and named none, so each session invented a filename and the
verdict pass went looking for a recapture that was never written there. Two
reviewer passes burned on that in the eval runs.

The parent now captures and recaptures to .impeccable/review/desktop.png and
.impeccable/review/mobile.png, and the reviewer reads those two first,
treating a brief-named path as the fallback for a parent that wrote elsewhere.

Prepared by an AI agent (Claude Code) under pbakaus's instruction.

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

* Lead Setup with the base directory the runtime reports

The rendered claude and codex skills opened with
`node .claude/skills/impeccable/scripts/context.mjs`, a project-relative path
that resolves in this repo and in nothing a user installs: a personal or
plugin install puts the scripts outside the project entirely. The working form
was already in the text, parenthesized, after the one that fails.

Setup now leads with `node <skill-base-dir>/scripts/context.mjs` and says once
that the base directory resolves every scripts-path command in the skill and
its references, leaving the project-relative path as the fallback for runtimes
that report no base directory.

Prepared by an AI agent (Claude Code) under pbakaus's instruction.

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

* Answer the Copilot review: brittle model assertion, missing review dir

Assert that {{model}} resolved rather than that it resolved to "GPT", which
belongs to PROVIDER_PLACEHOLDERS and can change without touching what the test
guards. And have the parent create .impeccable/review/ when the harness does
not, so a fresh project's first capture has somewhere to land.

Prepared by an AI agent (Claude Code) under pbakaus's instruction.

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

* Make the review-screenshot contract directory-based, not web-viewport-named

Two amendments to the recapture contract from review feedback:

1. The canonical location is the directory .impeccable/review/, one file
   per captured viewport; desktop.png and mobile.png are the web case,
   not the contract. Baking web-viewport names into the reviewer's spec
   would have hardened a web assumption into paths that a native
   (ios/android/adaptive) build cannot honestly write.

2. Precedence restored to explicit-beats-convention: paths the calling
   brief names are authoritative when the files exist; the canonical
   directory is where the reviewer looks when the brief names none or a
   named path is missing. This avoids stale canonical files from an
   earlier run silently winning over fresh explicit paths. The observed
   failure (the verdict round inventing a round-stamped filename) stays
   fixed: recapture happens over the same files, and invented filenames
   are still called out as pointing at nothing.

Assisted-by: Claude Code

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 19:26:43 -07:00
Paul BakausandGitHub fc05472a20 Restore reduced-motion animation guidance (#540)
* Restore reduced-motion build guidance

Restores the accessibility requirement and verification step to the animation playbook, with a regression test that keeps it on the build path.

Implemented and validated with OpenAI Codex assistance under standing maintainer authorization.

* Harden reduced-motion guidance regression

Normalizes CRLF input and accepts either reduced-motion spelling so the contract stays portable and intent-focused.

Implemented and validated with OpenAI Codex assistance under standing maintainer authorization.

* Anchor skill reference test to its module

Resolve the repository fixture path from the test module so the regression test is independent of the caller's working directory.

This change was prepared with AI assistance under maintainer authorization.

* Clarify reduced-motion guidance

Replace the double negative in the canonical animation guidance and keep the source contract aligned with the clearer wording.

This change was prepared with AI assistance under maintainer authorization.
2026-08-08 15:49:43 -07:00
Paul BakausandClaude Opus 5 aee6ce9352 Give the Live UI surface inventory one definition again
The list of Live chrome surfaces was inlined into live-browser.js as a
function-scope const when live/ui-core.mjs was deleted for having zero
in-repo references. It had one out-of-repo reference. The private
impeccable-site repo imports it at build time: its Live UI lab must hold
a snapshot for every surface Live defines, and the site build fails with
the surface name when one is missing. Inlining put the list out of reach
of every Node importer, so the site had to regex it back out of the
browser script, and the guard only kept passing because the site's
materialized copy of skill/ was stale.

A guard that reads a list the site itself maintains guards nothing, so
the fix is a real export rather than a better parser.

skill/scripts/live/ui-surfaces.mjs is now the single definition. The
browser-runtime constraint is unchanged and satisfied the same way the
command palette already solves it: live-browser.js is served raw and
injected as a classic <script>, so it cannot import an ES module. The
/live.js assembler serializes the module into
window.__IMPECCABLE_LIVE_UI_SURFACES__ in the prelude it already writes
for the token, port and vocabulary, and live-browser.js reads the global.
assembleLiveBrowserScript defaults the value from the module rather than
taking it from live-server.mjs, so the bundle carries the canonical
inventory by construction instead of by a caller remembering to pass it.

The emitted inventory is byte-identical to the inlined one.

tests/live-ui-surfaces.test.mjs pins both halves of the seam: the module
is the definition (live-browser.js must not redeclare it), the prefix the
module builds ids from matches the PREFIX live-browser.js hardcodes, and
the assembled bundle still carries the list. live-server.test.mjs gets
the matching integration check against a served /live.js.

One existing assertion changed. live-browser-regression.test.mjs checked
that the steer Send control is registered as live chrome by matching the
text of the inline literal's last line. That encoded where the list was
written, not what it contains; it now asserts membership in the imported
LIVE_UI_COMPONENT_IDS, which is the behaviour it was after.

Verified with the full default suite plus a live-e2e fixture run
(vite8-react-modal), so the overlay is exercised end to end in a browser.

AI-assisted via Claude Code under maintainer direction.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 14:53:20 -07:00
Forgeandplamb 5ce4a5c6b5 Add Hermes Agent as a supported provider
Impeccable now ships a Hermes-compatible bundle under dist/hermes/.hermes/skills/.
Hermes reads the Agent Skills spec as-is, so the bundle uses the four spec
frontmatter fields (name, description, version, license) plus metadata/compatibility
and drops the Claude/Codex-specific extensions Hermes would silently ignore.

The .hermes/skills/ tracked root and the regenerated pin.mjs mirrors will be
produced by .github/workflows/sync-generated-output.yml after this lands; per
AGENTS.md, generated harness churn stays out of feature PRs.

What a Hermes user gets:
- npx impeccable install --providers=hermes --scope=project writes
  .hermes/skills/impeccable/ into the cwd.
- npx impeccable install --providers=hermes --scope=user honors $HERMES_HOME,
  so a profile-scoped install (HERMES_HOME=~/.hermes/profiles/forge) lands in
  the active profile's skills dir, not the default ~/.hermes/. Cross-home
  HERMES_HOME inheritance is ignored so test isolation holds.
- /impeccable registers as a Hermes slash command and routes sub-commands via
  the Commands table in the skill body, since user-invocable / argument-hint
  are not honored by Hermes' skill loader.

What a Hermes user does NOT get, and why:
- No hook surface. Impeccable's PostToolUse/Stop anti-pattern detector on
  Claude/Codex/Cursor/Grok/GitHub does not translate to Hermes, which has no
  equivalent tool event lifecycle. The skill body still ships.
- No writeOpenAIMetadata, agentFormat, or emitHooks. Hermes has no per-skill
  tool ACL, no subagent on-disk format, and no hooks.json equivalent.

Verified end-to-end with hermes-agent v0.18.2: /impeccable polish and
/impeccable critique both load reference/<command>.md and return the
documented first step. parse_frontmatter accepts the generated SKILL.md,
scan_skill_commands registers /impeccable, and the full default test suite
passes (267/267 in the critical files; 0 fail across all suites).
2026-08-06 00:03:12 -07:00
667095d216 Harden the test strategy: self-verifying triggers, 40% faster runner, release guards (#501)
* test: harden the test strategy (triggers, runner speed, release guards)

Follow-ups from an end-to-end testing strategy review:

- Suite triggers are now auto-generated from each suite's own file list,
  so change-based CI can never miss a test file again (four files were
  unreachable by their own edits, and tests/lib/detector-bundle.test.js
  triggered core while running in detector). Two new meta-tests pin the
  invariant. Hand-written trigger patterns now carry only source paths
  and fixture dirs; palette dropped from the live triggers since no
  suite tests it.
- The node runner batches all files into one node --test invocation at
  concurrency 4 instead of spawning per file. Default suite drops from
  ~159s to ~100s; the live suite soaked clean three times.
- scripts/release.mjs gets its first tests: 12 scenarios spawning the
  real script inside a disposable git repo with a local bare origin,
  covering every refusal guard plus notes/tweet rendering, all under
  --dry-run.
- skill/scripts/live/ui-core.mjs deleted: zero references repo-wide,
  superseded by the July live rewrite, yet still shipping to users.
  cli/lib/download-providers.js annotated with its cross-repo consumers
  (impeccable-site Pages Functions) so it is not mistaken for dead code.
- CLAUDE.md gains an area-to-suite table for the opt-in suites a change
  owes; AGENTS.md syncs the plugin-e2e commands and obligations.

AI-assisted via Claude Code under maintainer direction.

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

* fix: exclude peeled tag lines from release-test origin cleanup

Copilot: git ls-remote --tags emits ^{} peel lines for annotated tags,
which are not deletable refs; --refs filters them so the cleanup loop
survives a future scenario that pushes an annotated tag.

AI-assisted via Claude Code under maintainer direction.

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

---------

Co-authored-by: Claude Code <noreply@anthropic.com>
2026-08-03 17:33:10 -07:00
Rex LorenzoandGitHub 14d2641685 Fix: keep the node runtime probe clear of cmd.exe metacharacters (#458)
Volta's Windows shims exec through `cmd /C`, which re-parses the argument
list, so the `>=` inside the probe's `node -e` payload was read as output
redirection. The command died with "The filename, directory name, or volume
label syntax is incorrect" before node started, the guard read that as a
missing runtime, and the hook it exists to protect was disabled on every
PostToolUse and Stop. A user on a supported Node 24 got a one-time notice
telling them to install Node 22, then silence.

Clamping with Math.min is the same floor test in the same ES5-only syntax,
with no character cmd.exe can claim. Verified through the Volta shim on Node
24.16.0 and 22.18.0 (exit 0) and against a real Node 20.6.1 binary (exit 1),
so the floor is unchanged. Adds a regression test asserting no `<`, `>`, or
newline reaches any generated `node -e` payload.

Upstream cause: volta-cli/volta#1791.

Prepared with AI assistance (Claude Code).
2026-08-03 15:02:29 -07:00
Paul BakausandGitHub d6a9891066 Share browser detector bundling (#498)
Centralize the browser-safe module set and source transformation so the browser and extension builders cannot drift.

AI assistance: This refactor was prepared by Codex under pbakaus's scheduled architecture-simplification authorization.
2026-08-03 14:51:46 -07:00
6d2af3f800 Guard the plugin loader contract that PR #494 exposed (#499)
* test: guard the plugin loader contract that PR #494 exposed

The agents manifest key shipped for months and silently loaded zero of
the four subagents; no validator looked at the generated plugin
manifest's shape and claude plugin validate never checks it. Three
layers now do:

- scripts/lib/validate-plugin-manifest.js pins the verified loader
  contract (KNOWN_LOADER_KEYS allowlist, no agents key, trailing-slash
  skills path from issue #86, every skill/agents/*.md shipped in
  plugin/agents/), unit-tested in tests/validate-plugin-manifest.test.js
  including a check of the real committed subtree.
- The same check gates bun run build next to the version-drift guard.
- tests/plugin-e2e.test.mjs installs the committed ./plugin subtree into
  a real Claude Code (sandboxed via CLAUDE_CONFIG_DIR in a temp dir) and
  asserts the component inventory: skill parses, all agents visible,
  hooks discovered. In the default suite; runs in about a second and
  skips cleanly when the claude CLI is absent, so CI is unaffected.

All three failed against the pre-#494 tree for the shipped reason
(Agents 0 of 4) and pass against current main.

AI-assisted via Claude Code under maintainer direction.

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

* fix: address PR review bot findings

- Copilot: guard collectPluginManifestFindings against valid JSON that is
  not an object (null, string, number, array) so a broken manifest is a
  finding instead of a build crash; unit test added
- Copilot: update the plugin-e2e header comment, the suite is in the
  default lineup rather than opt-in

AI-assisted via Claude Code under maintainer direction.

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

* fix: harden plugin E2E sandbox isolation

Bugbot: create the sandbox CLAUDE_CONFIG_DIR up front and redirect HOME
and USERPROFILE into the temp workDir too, so a CLI code path that
derives config or cache locations from the home directory instead of
CLAUDE_CONFIG_DIR still cannot touch the developer's real Claude config
when the default suite runs.

AI-assisted via Claude Code under maintainer direction.

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

* fix: agent parity check mirrors the build's emit rules

Bugbot: the shipped filename is `${claude-name || name}.md` and a
providers: list may exclude claude-code, so comparing raw source
basenames could fail the build on a renamed or provider-scoped agent
with a build:release hint that cannot fix it. The validator now derives
expected filenames the same way the transformer factory does (shared
parseFrontmatter, same providers gate) with unit coverage for renames,
name overrides, and provider-scoped agents.

AI-assisted via Claude Code under maintainer direction.

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

* fix: run the plugin E2E through a shell on Windows

Bugbot: the claude CLI is a .cmd shim on Windows and Node refuses to
spawn those via execFile without a shell, so the availability probe
always failed and the suite silently skipped there. Windows now invokes
through a shell with every argument double-quoted (temp paths routinely
contain spaces); the POSIX path is unchanged.

AI-assisted via Claude Code under maintainer direction.

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

---------

Co-authored-by: Claude Code <noreply@anthropic.com>
2026-08-03 13:13:37 -07:00
ingnicolaboccato-labandGitHub baa76cfd05 fix: stop emitting the "agents" key so the four subagents actually load (#494)
build.js derives plugin/.claude-plugin/plugin.json from the root manifest and
injects an `agents` array built from the generated agent files. In Claude Code
that array is exactly what stops them loading.

Verified on a throwaway local marketplace, across the plausible shapes:

  array of file paths (what build.js emits) -> 0 agents load, skills fine
  a string, e.g. "./agents"                 -> whole plugin fails to load
  array containing a directory              -> whole plugin fails to load
  key omitted                               -> all agents load, skills fine

Claude Code discovers agents/*.md on its own, and the identifier it uses is the
file name rather than the frontmatter `name`. So the key is not needed, and any
present form of it is worse than its absence.

Confirmed end to end on this repo: with the key emitted,
`claude plugin details impeccable` reports "Agents (0)"; with it omitted it
reports "Agents (4) impeccable-asset-producer, impeccable-documenter,
impeccable-finish-reviewer, impeccable-manual-edit-applier". The agent files
themselves are still copied by the existing copyDirSync a few lines below —
only the manifest key goes away.

Two things make this hard to notice: with a breaking shape
`claude plugin details` prints "Plugin not found" instead of a validation
error, and `claude plugin validate` does not catch it because it validates the
marketplace manifest, not the plugin manifest. The only reliable signal is the
"Agents (N)" line.

The same defect is reported against another project at
addyosmani/agent-skills#449, with the full reproduction.

Scope note: verified on Claude Code only. plugin/ is the Claude-Code / Grok
subtree, and the Grok manifest is written separately just below, so this does
not touch the other harnesses.
2026-08-03 11:55:14 -07:00
Abdul WahabandCursor 7d6109b723 Drop deprecated craft alias from the generated argument-hint
craft is a deprecated compatibility alias, but its SKILL_CATEGORIES entry
kept it advertised in every generated SKILL.md argument-hint. Unmapping it
removes it from the hint while the alias keeps routing through the router
table and command-metadata.json.

AI-assisted change (reviewed by maintainer).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 10:36:18 +05:00
Paul BakausandGitHub c91f3717d4 Merge pull request #466 from pbakaus/codex/remove-legacy-pattern-parser
Simplify curated pattern loading
2026-07-31 17:41:55 -07:00
Paul Bakaus 33f824b5b5 Clarify curated pattern source
Describe the catalog as independent of SKILL.md extraction rather than repository content, addressing Copilot's review feedback.

Prepared with Codex assistance under pbakaus's scheduled architecture cleanup authorization.
2026-07-31 17:19:33 -07:00
Paul Bakaus 358fc2e716 Simplify curated pattern loading
Remove the unreachable legacy SKILL.md pattern parser now that readPatterns uses the curated catalog exclusively.

Prepared with Codex assistance under pbakaus's scheduled architecture cleanup authorization.
2026-07-31 17:16:19 -07:00
Paweł Adamczak bf957452c4 Add Google Antigravity provider support 2026-07-31 12:02:37 +02:00
Paul BakausandClaude Fable 5 9d1b4bdfac Fix five stale rule counts and the validator blind spots that hid them
single-font's retirement made the detector 59 rules; both READMEs still
said 60 in five places, and the count validator reported clean because
'deterministic detector rules' puts a word the regex never expected
between the qualifier and the noun, and README.npm.md was never in the
checked file list. The regex now tolerates the detector infix, counts
qualified 'issues' claims, and README.npm.md joins the list.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-29 21:35:27 -07:00
Paul BakausandGitHub 5f4b58d06d Merge pull request #433 from pbakaus/live-v2-rewrite
Live v2: root manifest, mount-ack protocol, AST scaffolder, mechanical accept
2026-07-29 13:12:23 -07:00
Paul BakausandClaude Fable 5 fa1177ed9c Ship native subagent definitions for GitHub Copilot and Cursor
The github and cursor providers previously received only the generated
degraded/ inline fallbacks. Both harnesses support real custom subagents,
so the build now emits them from the same skill/agents/ source:

- GitHub Copilot: .github/agents/impeccable-<role>.agent.md with portable
  frontmatter only (name + description; omitting tools grants all tools,
  and Copilot has no documented model/effort/max-turns equivalents).
- Cursor: .cursor/agents/impeccable-<role>.md with name, description,
  model: inherit, is_background: false, and readonly derived from the
  agent's tool list (true only for the finish reviewer, which declares
  neither Write nor Edit). effort/max-turns are skipped because Cursor's
  effort option requires an explicit model id.

Agent bodies now also resolve {{scripts_path}} and strip rule markers in
the shared agentFormat pipeline, which fixes the previously unresolved
placeholder in the emitted Claude asset-producer agent.

The CLI installer places agents per scope: project installs write
<repo>/.github/agents/ and <repo>/.cursor/agents/; user-level installs
write ~/.copilot/agents/ (Copilot's user dir, not ~/.github/) and
~/.cursor/agents/, overwriting stale impeccable-* copies. Because
Copilot lets user-level agents shadow same-named project ones, a project
install warns when shadowing copies exist; Cursor gives project agents
precedence, so no warning there.

new-work.md and visualize.md extend their harness-naming clauses with
the Cursor and Copilot invocations. The degraded/ fallbacks keep
shipping for surfaces where the model still fails to delegate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 16:16:57 -07:00
Paul BakausandClaude Code 4ac54bebee fix: address PR review bot findings
cursor[bot] findings on #433:
- Nightly schedule no longer enables the paid opt-in suites: a schedule
  event has no diff base, so the change-detection fallback flagged every
  file-triggered suite, which would have billed the skill-behavior,
  accept-cleanup, and deepseek LLM suites nightly. The plan now pins the
  schedule event to deterministic suites plus the full live-e2e matrix,
  with a regression test.
- Dismissing the mount-error card no longer strands the session: while
  the bar is hidden in GENERATING the card is the only recovery surface,
  so dismiss now returns the state machine to PICKING (session and
  server truth survive for a later republish).

This work was produced with AI assistance (Claude Code).

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-07-27 15:19:58 -07:00
Paul BakausandClaude Code 17dabf4b7e Live v2: root manifest, mount-ack protocol, AST scaffolder, mechanical accept
A ground-up hardening of live mode, driven by a production session in a
nested-app monorepo that hit six distinct failure classes. Full design
rationale in docs/LIVE-REWRITE-PLAN.md; every Codex-reported failure now
has a mechanical fix and a regression test.

Roots: live/roots.mjs resolves appRoot/repoRoot/contextRoot once at boot
(keyed on dev-server configs, not monorepo brand markers), persists a
manifest, and every live CLI re-anchors onto it at startup, so a helper
run from the wrong directory can no longer fork session state. Context
files are discovered upward to the git root.

Render truth: variant_mounted / variant_mount_failed events give the
journal per-variant mount state; failures reach the agent's poll queue,
raise a persistent error card with Retry (no more localStorage wipe), and
an attach probe names root/dev-server mismatches explicitly. The browser
rehydrates from the server when localStorage is gone.

Svelte: the scaffolder now parses with the app's own svelte 5 compiler.
Control flow survives (an each collection crosses the contract as one
structured prop), keyed each blocks hydrate synthetic keys, and anything
a detached preview cannot support falls back to source-preview instead of
shipping a wrong scaffold. Preview modules live in per-publish revision
directories, defeating stale transform caches.

Accept: CSS is reconciled, not appended. Matching selectors are replaced,
params bake from params.json kinds, the compiler's unused-selector pass
prunes superseded rules (pre-existing dead rules protected), a selector-
loss postcondition refuses any write that would drop hand-written rules,
and live-complete refuses to finish while live plumbing remains in source.

Also: framework registry (live/frameworks/) with a crash-safe injection
journal, session-store snapshot caching with read-only reads, protocol
enum consolidation, steer Send button, honest DESIGN-panel empty states.

Testing: new unit suites (roots, AST scaffolder, accept CSS, accept
pipeline, framework conformance); e2e now fails on preview-tree 404s,
proves computed-style mount for every variant, drives the Tune panel
through baked params, and injects failures (broken mounts, republish,
storage loss). New runtime fixtures: monorepo-nested-vite (repo root !=
app root) and vite8-sveltekit-stateful (each blocks + state). Nightly
full-matrix cron. An independent adversarial review pass preceded this
commit; its blocker and major findings are fixed and regression-tested.

This work was produced with AI assistance (Claude Code).

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-07-27 15:09:40 -07:00
Vinaywho c9c0fc887b Merge remote-tracking branch 'upstream/main' into fix/detect-system-chrome-gpu-window
# Conflicts:
#	scripts/test-suites.mjs
2026-07-27 15:13:14 +05:30
Paul BakausandGitHub 5e572c8b8a Merge pull request #423 from pbakaus/hook-guard-unsupported-node
Stop the design hook erroring on a node too old for ESM
2026-07-26 18:12:52 -07:00
fd9076f4f0 Enforce the engines floor in the probe instead of a capability check
The probe asked whether node could load ESM, while the notice promised a
Node 22 floor and package.json engines declares >=22.12.0. Reviewers kept
flagging the gap, and they were right to: a 14.18-to-21 runtime passed the
probe on the strength of one import while the hook and its detector bundle
are only ever exercised on the engines floor, so "can load our code" was a
weaker claim than the one being made for it.

Check the floor directly: parseInt(process.versions.node) >= 22, in
ES5-only syntax that parses on any node old enough to fail it. Probe and
notice now derive from one NODE_MAJOR_FLOOR constant, so they cannot
disagree, and the archaeology about node: scheme support and pre-15
unhandled-rejection semantics goes with the import it explained.

Add the missing contract test: every generated hook command carries the
probe, the notice appears exactly where a harness can render it (Claude
and Codex, project and plugin), and the expected floor is read from
package.json engines rather than repeated by hand.

Verified against a fake pre-22 node, no node, and a real node: one notice
then the marker holds it silent, exit 0 in every failure shape, and the
hook's own exit code still passes through on a supported runtime.

Co-Authored-By: Claude Fable 5 (via Cursor) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-26 22:17:19 +05:00
Abdul WahabandClaude Opus 5 86cdf528c5 Probe the import the hook actually uses, and fail closed on rejection
Greptile flagged that the probe does not enforce the Node 22 engines floor.
Two parts to that, and they land differently.

The real defect is narrower and worse than stated: the hook closure imports
`node:fs`, `node:os`, `node:path` and `node:url`, and the `node:` scheme needs
14.18, so a bare `import('fs')` probe passed on 12 and 13 and those runtimes
then died on the real import, which is the banner this branch exists to remove.
Probing `node:fs` closes that. The added `.catch(()=>process.exit(1))` is load
bearing rather than tidiness: before Node 15 an unhandled rejection is only a
warning and the process still exits 0, so a rejected probe would have read as a
pass on exactly the versions in question.

Not enforcing 22 is deliberate and stays. The probe asks whether this runtime
can load our code, not whether it is a supported one, so a 14.18-to-21 runtime
that works today keeps working rather than being silently switched off. The
notice names 22 because that is the version worth installing, and it only ever
reaches someone whose runtime already failed the probe, so no user is shown a
threshold that contradicts what ran.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 21:20:26 +05:00
Abdul WahabandClaude Opus 5 4f999ceff8 Give Codex the notice too; its hook reference documents systemMessage
Commit 8397d532 took a reviewer's word that Codex expects hookSpecificOutput
and dropped its notice on that basis. Codex documents `systemMessage` for
PostToolUse and Stop as text shown as a warning in the UI or event stream,
the same field Claude Code reads, so the notice belongs there and the earlier
comment asserted something unverified.

Checked the rest against their own references while here. Cursor's preToolUse
output is permission-shaped and its user_message renders only when the action
is DENIED, so warning would mean blocking the edit. Grok treats PostToolUse
and Stop as passive events and ignores stdout outright. Copilot's contract is
unconfirmed. Those three keep the probe alone, which is a verified limit now
rather than an assumption.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 21:10:41 +05:00
Abdul WahabandClaude Opus 5 0c19098754 Guard the remaining harness manifests against a dead node runtime
Bugbot caught the Codex plugin builder still invoking node directly, and the
same reasoning covers GitHub Copilot and Grok Build: all three shipped the
exact failure this branch exists to stop, and sat visibly inconsistent with
their guarded siblings.

Route them through guardedNode with no notice, matching Codex and Cursor.
GitHub gains a second property from it: outside a git repository
`$(git rev-parse --show-toplevel)` expands to nothing, so the old command
handed node a path that could not exist and failed the turn. The file test
now short-circuits that to exit 0.

Every builder carries the probe; only the two Claude manifests carry the
notice, which is the only harness whose response shape is confirmed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 20:58:49 +05:00