mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-11 21:57:14 +03:00
bd6964c35b9dd7332e00a8db929e98d2b39acc89
32
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
bd6964c35b |
Trust the OS certificate store for engine HTTPS requests (#757) (#759)
The engine verified TLS against the Mozilla roots bundled through webpki-roots only, so behind a TLS-inspecting proxy (Aikido, Zscaler, Netskope) whose root lives in the OS trust store, `impeccable update` and `install` failed with `invalid peer certificate: UnknownIssuer` while curl and npm on the same machine succeeded. crates/context/src/http.rs builds one rustls ClientConfig per process: the OS trust store (rustls-native-certs: Keychain, Windows store, the OpenSSL paths on Linux) merged with the bundled roots. A union, not a replacement, so a container without ca-certificates or a store that fails to load still verifies exactly as before. SSL_CERT_FILE and SSL_CERT_DIR replace the OS store the way they do for OpenSSL and curl. Every HTTPS call site (bundle and signature downloads, /api/version, /api/commands, the roll API, image generation) builds its agent from this module; the plain-HTTP live-server calls on localhost are untouched. Verified against a local HTTPS server signed by a throwaway CA: trusted through SSL_CERT_FILE the update check reaches it; without it the same server is rejected as UnknownIssuer; with SSL_CERT_FILE pointing at that CA or at a missing file, impeccable.style still verifies through the bundled roots. cargo test --workspace and the oracle corpus (832) pass. Written with AI assistance (Claude Code). Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
e2ff625b63 |
The Rust engine: one binary replaces every script and the JS detector, fully open (#714)
* Add oracle harness: verb goldens and function-level vectors
Records stdout/stderr/exit/files for every impeccable verb over a fixed
corpus and replays them against an alternate implementation. Adds a loader
hook that captures per-function call vectors from the pure engine modules.
Prepared with AI assistance (Claude Code).
* Oracle: hook, hook-before-edit, hook-admin cases and goldens
Prepared with AI assistance (Claude Code).
* Add docs/CLI-CONTRACT.md: observable behavior of every impeccable verb
Prepared with AI assistance (Claude Code).
* Oracle: context/doctor/pin/surface-brief/critique/palette/embed/signals/csp/seed/genimg/question cases and goldens
Prepared with AI assistance (Claude Code).
* Oracle: live-mode cases and goldens (roots, inject, wrap, insert, accept, session, manual edits, daemon)
Prepared with AI assistance (Claude Code).
* Oracle: mask the binary path before HOME; export launcher env to the binary
Prepared with AI assistance (Claude Code).
* detect: set process.exitCode instead of exiting after the final write
process.exit() right after a large piped stdout write truncated JSON output
at the pipe buffer boundary; found by the oracle harness. Re-record the six
directory-scan goldens that had captured the truncation.
Prepared with AI assistance (Claude Code).
* Oracle: normalize the hook-admin command in both runtimes' forms and audit chars
Prepared with AI assistance (Claude Code).
* Skill text: invoke the impeccable launcher instead of node scripts
Every `node {{scripts_path}}/<name>.mjs` becomes `{{scripts_path}}/impeccable <verb>`
(context-signals -> signals, hook-admin -> hooks). Setup step 1 drops Node, points
Windows shells without sh at impeccable.cmd, and says the launcher runs a
self-contained binary. allowed-tools follows.
Prepared with AI assistance (Claude Code).
* Scripts dir: replace the Node scripts with the impeccable launcher
skill/scripts keeps command-metadata.json and the page JS; every .mjs entry
point, lib/, and live/ are gone (the binary owns those verbs). Adds the POSIX
launcher, impeccable.cmd, VERSION (copied from the new root ENGINE_VERSION),
scripts/fetch-engine.mjs (bun run fetch:engine) to pull the pinned binary
into skill/scripts/bin/<os>-<arch>/, and gitignores that bin dir.
Prepared with AI assistance (Claude Code).
* Build: ship the launcher instead of bundling the JS engine
readSourceFiles no longer copies cli/engine into the skill; the scripts
payload is the launcher (executable bit preserved through dist, plugin/, and
universal.zip), impeccable.cmd, VERSION (synced from ENGINE_VERSION on every
build), the page JS, and command-metadata.json. Hook manifests call
`<scripts>/impeccable hook` behind an existence guard (Codex adds a
commandWindows sibling calling impeccable.cmd; Cursor runs hook-before-edit;
GitHub keeps the git rev-parse form; Grok mirrors Claude); the Node probe and
systemMessage notice are gone. build:release fetches the pinned engine for
every target (lenient) and stages bin/<os-arch>/ into the dist skill copies
after root harness dirs and plugin/ were synced, so git-delivered trees stay
launcher-only. The detection-rule count check reads the vendored
extension/detector/antipatterns.json and is skipped when absent.
build:browser is a stub; the codex prefix rewrite leaves
`{{scripts_path}}/impeccable` alone.
Prepared with AI assistance (Claude Code).
* CLI: turn the impeccable npm package into a platform-binary shim
cli/engine, cli/lib, and cli/bin/commands are gone; their behavior lives in
the engine binary. cli/bin/cli.js now resolves the binary from IMPECCABLE_BIN,
the @impeccable/cli-<os>-<arch> optional dependency (templates under
cli/platform-packages/, published by the engine release), the
~/.impeccable/bin/<version>/ cache, or a checksum-verified download, and
execs it. package.json drops the engine dependencies and the library
exports; puppeteer moves to devDependencies for the icon scripts.
README.npm.md describes the shim.
Prepared with AI assistance (Claude Code).
* Tests: gate behavior on the oracle and the engine binary
Unit tests of the deleted Node scripts and the JS detector are removed;
their behavior is pinned by tests/oracle goldens (frozen JS behavior plus
reviewed deltas) and the engine's own tests. tests/oracle.test.mjs replays
the corpus against the binary (IMPECCABLE_BIN or skill/scripts/bin/<target>/,
via tests/lib/engine-bin.mjs) and skips cleanly without one; the framework
fixture sweep drives live-inject, live-wrap, and detect-csp through the
binary the same way. record.mjs learns --bin. The function-level vectors
under tests/oracle/vectors/calls are committed as the frozen snapshot they
can no longer be regenerated from. Suites: core trimmed to build and
transformer tests, oracle added to the default run, detector/live reduced to
packaging and reference checks, the live-e2e helper tests move to the opt-in
live-e2e lane pending its retarget, cli-remote-e2e is an empty placeholder.
Prepared with AI assistance (Claude Code).
* Docs: describe the launcher, the engine pin, and the oracle gate
CLAUDE.md gains an Engine binary section (launcher lookup order, ENGINE_VERSION,
untracked binaries, how tests get one, the oracle as behavior gate, what stays
JavaScript) and drops the Node-script and JS-detector descriptions; the CLI
and detection-rule sections point at the shim and the engine repo. README.md
states the skill needs no runtime and lists the launcher-based hook commands;
AGENTS.md follows. CLI-CONTRACT.md's intro notes the scripts it quotes are
the recorded source, not the tree.
Prepared with AI assistance (Claude Code).
* Tests: tighten the hook command guard assertion
Prepared with AI assistance (Claude Code).
* Oracle: re-golden 46 cases for the engine's own command names; record them in DELTAS.md
Prepared with AI assistance (Claude Code).
* Build: ship launcher-only release zips by default
IMPECCABLE_BUNDLE_ENGINE=1 opts in to staging the engine binaries into the
dist skill copies. Bundling every target into every provider copy put
dist/universal.zip near 340 MB, past the 25 MB Cloudflare Pages file cap
that impeccable install downloads through.
Prepared with AI assistance (Claude Code).
* Tests: drive the live-e2e orchestrator through the engine binary
The session, fake-agent loop, steer test, and manual-edit probe spawn
<binary> <verb> (live-server, live, live-inject, live-wrap, live-insert,
live-accept, live-poll, live-complete) resolved by tests/lib/engine-bin.mjs
instead of node skill/scripts/live-*.mjs; the completion typing the agent
imported from the deleted live/completion.mjs is a small local helper. The
live-e2e helper unit tests move back into the default live suite (the steer
loop skips without a binary).
Prepared with AI assistance (Claude Code).
* Tests: run new-work-e2e through the engine's serve-question and generate-image verbs
Prepared with AI assistance (Claude Code).
* Tests: point the skill-behavior harness at the launcher and engine binary
The bash tool exports IMPECCABLE_BIN so the staged skill's launcher runs
without a download; scenarios assert on 'impeccable context' instead of
context.mjs and skip without a binary.
Prepared with AI assistance (Claude Code).
* Tests: note what plugin-e2e validates before and after the generated-output sync
Prepared with AI assistance (Claude Code).
* Oracle: record the engine's 'wasm-unsafe-eval' CSP meta patch as a reviewed delta
Prepared with AI assistance (Claude Code).
* Rebase reconciliation: fold main's post-freeze work into the swapped tree
The rebase onto origin/main brought changes whose JS engine halves left the
tree with the swap. This commit reconciles what survives:
- Suite map: register main's comp-fidelity unit tests (build-phase,
comp-diff, font-match, hero-checks) in the core suite and
live-browser-ignores in the live suite.
- Payload guard: the skill scripts payload now allowlists the comp-fidelity
build pipeline (comp-spec/comp-diff/build-phase/font-match and their libs),
the one Node toolchain that has not moved into the engine.
- Drop skill/scripts/live/project-ignores.mjs, lib/live-path-globs.mjs, and
their test: they import hook-lib/live-inject/impeccable-paths, which the
swap deleted, and their consumer (the JS live server) is the engine now.
- skill text: the comp pipeline's calls to engine verbs (generate-image,
embed-prompt) use the launcher spelling.
- Oracle: re-record 17 detect goldens over the fixture set main changed
(oklch #592, color-mix #578, 1D grid #615, the two comp-fidelity rules)
and record the gap in DELTAS.md; those JS rule changes are not yet ported
to the engine, and the goldens pin its current behavior.
bun run test (oracle included) and bun run build are green on this tree.
AI-assisted change: implemented with Claude Code.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaJv2c4oN8wS7Ttq4XRqyx
* Launcher: engine-probe PATH validation, working .cmd download path; CI: drop stale path, add oracle job
Byte-identical copies of the engine repo's launchers (engine main
af7572c): the retired 3.x npm CLI on PATH or in ~/.impeccable/bin is
rejected by the engine-probe handshake instead of hijacking every verb;
impeccable.cmd's download path is rewritten as straight-line goto flow
(the parenthesized blocks expanded %url%/%cached% at parse time, making
it dead code) with certutil sha256 verification and a windows-arm64 ->
x64 asset fallback; the final error points at the release download
instead of npm i -g (npm still serves the 3.x CLI).
ci.yml: the generated-output check no longer diffs the deleted
cli/engine/detect-antipatterns-browser.js, and a new oracle job fetches
the pinned engine (bun run fetch:engine) and replays tests/oracle/
against it. The job is continue-on-error with a loud warning until the
first engine release exists; flipping it to required is a release-time
toggle, documented in the workflow.
Verified here: sh -n on both launcher copies, bun run build green, full
oracle replay against the rebuilt engine binary green (770 pass, 0
fail), and a launcher behavior test proving a fake 3.x CLI on PATH is
skipped while the download + checksum chain completes against a local
file server.
Prepared with AI assistance (Claude Code).
* Oracle: restore detector goldens to post-fix behavior after the engine ports
The Aug 17-31 detector fixes (oklch parsing, color-mix nested hex, 1D grid
pass, comment stripping, root-relative linked stylesheets, URL userinfo
redaction, inert ignore-value refusal) and the comp-fidelity rules
organic-clip-path / buried-raster are ported to the engine. Re-records the
gap-pinning detect goldens from the fixed binary (glow.html included: its
.photo-opaque-grad column now carries the buried-raster finding it was
written for), replays the frozen checkHtmlPatterns call vectors through the
last JS engine state in history (db1462b9^; args untouched, 14 of 101
results moved), and rewrites the DELTAS gap section into the landed-ports
note. Each re-recorded json fixture golden byte-matches that JS state's
output; oracle: 770 pass, 0 fail.
Prepared with AI assistance (Claude Code).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaJv2c4oN8wS7Ttq4XRqyx
* Oracle: pin the Aug 17-31 verb fixes ported to the Rust engine
New cases: hook-session-grok-edit-then-stop (Grok Build camelCase envelope,
end_turn/shutdown/stopHookActive Stop handling,
|
||
|
|
f2f9958b3d |
Fix: fail URL scans when the browser is unavailable (#711)
* Fix URL scan failure exit codes Return exit 1 when browser setup or a URL scan fails, including partial multi-target scans, while preserving JSON findings output. Document the detector exit contract and cover isolated installs without Puppeteer.\n\nAI assistance disclosure: Codex implemented and tested this fix under maintainer direction. * Fix local target failure exit codes AI assistance disclosure: Codex implemented and tested this fix under maintainer direction. * Handle unreadable detector targets AI assistance disclosure: Codex implemented and tested this fix under maintainer direction. * Report unreadable detector directories AI assistance disclosure: Codex implemented and tested this fix under maintainer direction. |
||
|
|
f3df3ffe40 |
Add Veto harness support (#675)
Adds Veto detection, provider transforms, installation paths, documentation, and regression coverage.\n\nAI-assisted maintainer repair, review, and validation by Codex under maintainer direction. |
||
|
|
33367e2297 |
Add Hermes Agent to supported harness docs (#672)
Documents the existing Hermes Agent provider, including profile-aware global installation and project trust behavior. Contributed by the Hermes Agent maintainer.\n\nAI-assisted change: repaired, validated, and merged by Codex under maintainer direction. |
||
|
|
34ef9ac2c4 |
Rewrite the comp-led build around the phase gates; reviewer reads the diff first
new-work.md section 6 becomes the spec / plates / hero / sections / motion / responsive phase list, each closed by build-phase.mjs advance; the reproduction and hero-checkpoint prose that asked the model to compare from memory is gone. visualize.md's inventory, medium gate, and produce sections collapse into the spec and plate mechanism. The finish reviewer takes the state file and comp-diff reports as inputs and starts its fidelity matrix from the measured verdicts. docs/COMP-FIDELITY.md records the design. AI-assisted (Claude). Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
5dfeba6d3e |
Bump @babel/parser to 8.0.4 and raise Node floor (#430)
Upgrade @babel/parser from 7.29.7 to 8.0.4 and raise the repository Node 22 minimum from 22.12.0 to 22.18.0 across package metadata, CI, and npm documentation. No parser API migration was required. Validated with the full local suite and refreshed GitHub CI on Node 22.18.0 and Node 24. Prepared and validated with AI assistance from OpenAI Codex under maintainer instructions. |
||
|
|
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> |
||
|
|
6ff9f957ac |
Add radial-spotlight-glow detector rule
Flags the decorative low-opacity chromatic radial-gradient "spotlight" washed behind a hero or section and fading to transparent, an AI-slop reflex the saturated radial-halo gate lets slip (e.g. rgba(80,111,255, 0.26) -> transparent on a mobile hero). Gates: a non-repeating radial-gradient whose last stop is transparent, whose visible stops are all low-opacity (alpha < 0.45) with at most two of them, at least one chromatic (channel spread >= 24 exempts neutral vignettes), on a decorative-scale surface (width >= 240, height >= 160, exempting badges/avatars/small lights). The alpha band is disjoint from radial-halo (>= 0.7), so the two never double-report. Wired into both element loops (static-html + injected browser) with the pure checkRadialSpotlight shared by both adapters. TDD fixture with 5 flag / 9 pass shapes. Browser-path sweep over the eval corpus: 29 hits on 11 pages, 0 false positives. Count 59 -> 60. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
77c7d8e0fc | Refine product and visual work lifecycle | ||
|
|
ed7a6fbe4e |
detector: text-occlusion + first-viewport-column-overflow (57 -> 59)
Two browser-engine quality rules, both warning severity. text-occlusion / element-overlap fires on three shapes: an opaque decorated box painted over a text element (elementFromPoint confirms real coverage, box >= 30%), one text run buried under another when at least one side is a positioned layer (text >= 45%, so line-box leading bleed between stacked flow blocks does not count), and an inline element whose opaque fill leaks past its line onto a neighbour (the class-name collision bug). A large headline whose edge overhangs a bounded content card is caught as an element collision even when the text stays on top. Gradient scrims, decorative SVG emblems, fixed/sticky overlays, floats, and raw image backdrops (contrast territory, deduped against the pixel low-contrast rule) are exempt. first-viewport-column-overflow fires when a multi-column opening section runs one column past 140% of the viewport while a sibling fits inside one screen, the stretched-hero signature. Single-column pages and full-page heroes with no fitting sibling are exempt. Validated: fires on the diagnosed repros, clean across a 60-sample sweep. Fixtures + browser tests added. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
dc0b25d393 |
detector: hero pulsing-dot promotion, nav-CTA contrast gap closure, shape-assembled-illustration (56 -> 57)
Item 1 (hero liveness theater):
- pulsing-dot now merges declarations per selector across rule blocks
(cascade-approximate), descends into media queries, and strips
prefers-reduced-motion: reduce overrides before the predicate runs.
Catches the shipped split-block constructions (size in the base rule,
animation added later or inside a no-preference media block).
- Dots whose element sits inside a header/nav landmark are promoted to
error severity (string-level landmark ranges in both engines); the
browser engine additionally promotes dots resting in the first ~900px.
- blinking-cursor findings in the first ~900px or inside header/nav are
promoted from advisory to warning.
- Per-finding severity overrides now flow through static-html,
browser-injected serialization, and detect-url.
Item 2 (nav-CTA contrast constructions):
- The a24-opus 01/002 header CTA already fires (specificity cascade +
oklch + var() all resolved); systematic sweep found two remaining
escapes and closes both:
- own gradient background on a SAFE_TAGS element (checkColors styled-
control exception now treats an own gradient as an own surface,
contrast measured against the worst stop)
- ::before/::after full-cover surface (static cascade marks pseudo
surfaces; browser adapter reads the pseudo computed style) so text is
measured against the surface the browser actually paints
- nav-cta-constructions fixture locks all eight computable construction
families; background-image: url() remains unflaggable by design.
Item 3 (shape-assembled-illustration, slop/advisory):
- New rule for large inline SVGs composing a pictorial scene from >= 8
primitive shapes at >= 200x200 intrinsic size with >= 3 distinct fills.
Charts (axis labels), stroke-only technical drawings, icons/logos
(small explicit size), and pattern-tiled backgrounds are exempt.
1.8 percent fire rate over the 3069-sample eval corpus, all verified
pictorial scenes; zero fires across val-a22/val-a24.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
c98f5d42ed |
detector: script-error, content-hidden-at-rest, edge-flush-cards + chip contrast and inline-overflow widenings (53 -> 56)
Three new rules and three widenings, all from confirmed eval-corpus
escapes found by eye:
script-error (quality, error severity, URL engine): pageerror listener
attached before goto catches uncaught exceptions AND parse errors (a
syntax error fires during the initial parse, long before load). Deduped
by message, capped at 3. A JS typo was silently deleting whole pages.
content-hidden-at-rest (quality, error, URL engine): after the main
at-rest scan, an instant-scroll reveal sweep (bypasses scroll-behavior:
smooth, which silently defeated the first sweep design) gives every
IntersectionObserver reveal its chance to fire, returns to top, then
measures the share of text characters still at opacity 0 / visibility
hidden. display:none / [hidden] / aria-hidden subtrees stay out of the
denominator. Fires above 30% with a 200/150-char floor. Calibration on
30 corpus samples: broken repro holds 83% after the sweep, all clean
samples (including 0.75-0.93 at-rest reveal pages) drop to <= 7%.
edge-flush-cards (quality, warning, browser): cards with their own
opaque background or 2+ borders inside a horizontal scroller, flush
against one edge of the clip box at rest (< 8px, > -24px so deliberate
mid-card peeks stay exempt) while keeping a gutter on the other side.
Grouped per scroller. Repro: transit-mobile pager whose first snap
panel is 407px wide inside a 390px clip. New --viewport WxH CLI flag
makes mobile-width URL scans reachable (--viewport 390x844).
Chip/badge contrast widening: the SAFE_TAGS styled-button exception in
checkColors now covers any text-bearing element painting its own opaque
background at >= 9px font, not just a/button. The shipped miss: a span
SEV-2 chip whose white text lost a specificity fight and rendered
muted-on-red at 1.2:1. Static adapter also resolves var() own-bg via
the custom-property map so the gate engages on token backgrounds.
background:none cascade fix: the background shorthand now resets
background-color/-image when it names neither (and no var()). Exposed
by the chip widening: pre code { background: none } left an earlier
surface color standing and manufactured 1.1:1 phantom findings.
text-overflow inline-owner widening: inline elements have no client
geometry (clientWidth 0) so the scrollWidth path never saw them, and
their block parent owns no direct text. New branch measures the inline
rect against the nearest block container's padding box (16px floor,
transform-path exempt). Repro: nowrap span.v spilling 45px past its
grid cell.
The round-3 nav-CTA contrast escape (val-a22-opus obs 003 header CTA)
was verified already covered at HEAD by the earlier parseAnyColor
oklch fallback; both engines fire 3.6:1 on the repro, no change needed.
FP sweep across 36 val-a21/a22/a23 samples: new rules fire only on
their repros (script-error also catches a second genuinely broken
sample); static-engine delta is limited to the chip repro plus two
borderline-but-real chip findings on one sample.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
cc8906ecaa |
detector: add heading-rhythm and blinking-cursor rules (51 -> 53)
heading-rhythm (quality): a heading binds to the content it introduces, so its rendered space above must exceed its space below. Browser-only: measures real getBoundingClientRect gaps (margin collapsing, flex rows, and section padding make authored margins untrustworthy), merges eyebrow labels into the heading cluster, requires same-column edges, and exempts first-in-container headings, bounded bands, and small cards. Fires only when 2+ headings on a page invert the rhythm. blinking-cursor (slop, advisory): a decorative blinking caret (solid block, underscore bar, or block glyph) bound to an infinite blink animation in the landing region of a page. Real editable surfaces (contenteditable, role=textbox, inputs) are exempt; round pulsing dots stay with the pulsing-dot rule. Verified against eval corpus repros: heading-rhythm fires on the val-a18 observability sample Paul flagged (6 headings, 0px above vs 40px below) and blinking-cursor on the val-a19 hero terminal cursor; 10 other samples across both runs stay clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
7a99e1725d |
detector: four human-review rules — nav-CTA oklch contrast, numbered section labels, floating side-tab stripes, repeated card text
Four gaps found shipping in Opus 4.8 eval samples during human review: 1. low-contrast (extended): the browser adapters parsed text/own-bg colors with parseRgb only, so Chrome's oklch()-serialized computed colors silently skipped every contrast check — a flat dark-on-dark nav CTA (broader nav selector beating the button class) shipped at 1.5:1 undetected. checkElementColorsDOM and readOwnBackgroundColor now fall back to parseAnyColor. Near-threshold ratios print two decimals so a 4.497 finding no longer reads "4.5 needs 4.5". 2. NEW numbered-section-labels (slop, advisory): tiny (<=13px) styled numeric index labels riding beside section headings, repeated across 2+ sections with distinct indices. Sibling of repeated-section-kickers (which deliberately excludes bare numeric labels); handles both the direct prev-sibling shape and label-before-heading-wrapper shape. List/nav/table/card-item numbering is exempt. 3. side-tab (extended): the vertical pseudo-element stripe scan required the stripe to touch both corners (top/bottom 0 or height 100%), so a left accent bar inset a few px from each end evaded it; small end insets (<=20px each) now count. Added a browser-side pseudo-element check (getComputedStyle(el, '::before'/'::after')) since runtime- assigned custom-property colors are invisible to the text scanner. Selection-state exemptions stay as narrowed: only aria-selected=true / aria-current / active-class markers exempt, plus button/link affordances on the horizontal variant. 4. NEW repeated-container-text (quality): the same literal string (>=4 chars, contains letters) rendered 3+ times at 3+ structurally distinct positions inside one bordered/elevated container. Parallel/templated repetition (table cells, calendar grids, nav lists, identical sibling rows) never counts — structural signatures, not word lists. Verified: each rule fires on its repro sample via the file:// browser scan; clean eval samples add no new findings (the new low-contrast hits on other samples are genuine sub-AA oklch button pairs). Full test suite green; browser bundle regenerated; README/homepage rule counts bumped 49 -> 51 (docs-integrity test enforces them). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
f690785c3e |
docs: 49 deterministic rules (radial-halo, marquee)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
cfdb7d4c81 |
detector: catch pseudo-element side stripes; add pulsing-dot rule
Two gaps surfaced by human eval review of real artifacts: 1. side-tab missed the pseudo-element variant. The accent stripe drawn as an absolutely-positioned ::before/::after (left/right: 0, top+bottom: 0 or height: 100%, narrow width, colored background) uses no border property at all, so neither the element-level border checks (pseudo elements never enter the static cascade or DOM walk) nor the border-left/right regexes could see it. New scanCssTextForPseudoStripe scans stylesheet text for that shape, mirroring the border rule's gates: >= 3px thick (<= 12px), chromatic fill (var()-resolved, neutral dividers skipped), full height against a side edge, with the blockquote/prose exemptions preserved. 2. New pulsing-dot rule (slop): small circular "live" indicator dots (<= 16px, border-radius >= 40% or pill values) bound to an infinite animation whose keyframes vary opacity, scale, or box-shadow — or pulse/blink/ping names when the keyframes aren't in the scanned text — plus the Tailwind animate-ping/pulse + rounded-full + tiny-size utility combo. Rotation-only keyframes (spinners) never flag, including when they hide behind a pulse-like name. Both scanners live in checkHtmlPatterns, so the static-html engine and the browser bundle share the same detection path. Browser/extension bundles regenerated; docs rule count bumped to 47. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
0d1c34e9d0 |
Fix: support Node 22 CLI installs (#361)
Lower the CLI engine floor to Node 22.12 so npx no longer falls back to stale 2.x releases for Node 22/23 users. Add Node 22.12 CI coverage while preserving the stable required test check, and document the 3.2.1 CLI release notes including the detector and installer fixes already waiting on main. AI-assisted-by: Codex |
||
|
|
f40e2f8f0a |
Add mechanical pre-scan for typeset and layout (#345)
* Add mechanical pre-scan for typeset and layout commands. Introduce --scope filtering, layout/type rule scopes, DESIGN.md font-size validation, and pre-scan steps in the skill references so agents run detect before LLM judgment. Fixes #149 Co-authored-by: Cursor <cursoragent@cursor.com> * Add isolated sub-agent orchestration for typeset and layout pre-scans. Run the mechanical detector and visual assessment in parallel sub-agents so deterministic findings cannot anchor LLM judgment, matching the critique pattern Paul requested on PR #345. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix: reject bare --scope so detect never scans unscoped by mistake. When --scope had no value, the CLI dropped the flag and ran a full scan instead of failing, which could silently use the wrong rule set during typeset/layout pre-scans. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix: require both typeset and layout assessments in sub-agents. Close a loophole where agents ran only the mechanical pre-scan inline by interpreting "running both" as permitting one inline assessment. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Abdul Wahab <abdulwahab@Abduls-MacBook-Pro-2.local> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
f5c1bd65ae |
Add codex-grid-background detector rule (#328)
* Add codex-grid-background detector rule Detects the Codex two-axis grid-line background tell: a single background value carrying two or more hairline `linear-gradient(... 1px, transparent 1px)` layers (one per axis), usually paired with a repeating `background-size` cell. Gated behind --gpt like the sibling codex tells, off by default. Counts hairline stops within a single background declaration (not across the page) so unrelated single-axis ruled lines don't add up to a false flag, and matches the stop directly rather than parsing whole gradient layers, since colors like oklch(...) carry nested parens. Extends the gpt-tells fixture with one flag case and two pass cases (single-axis rule, two-color blend), regenerates the browser detector bundle, and bumps the rule count 44 -> 45. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Require tiling background-size for codex-grid-background Address review: two hairline gradients alone draw a fixed crosshair, not a grid. Scope detection to a single style block (CSS rule body or inline style attr) and require both >=2 hairline stops AND a tiling `background-size` px cell in the same block, matching the skill rule's "plus background-size" wording. Add a crosshair-without-tiling pass case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Scope codex-grid-background hairline count to background values Address review: count hairline stops only inside background/background-image declaration values, not the whole style block, so a hairline in an unrelated property (mask-image, border-image) can't stand in for the grid's second axis. Add a bg+mask-image hairline pass case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
2f9dc05978 |
Give GitHub Copilot equal prominence in harness listings (#280)
Audit of every user-facing surface that enumerates supported harnesses found GitHub Copilot missing or buried in several. Bring it to parity with Claude Code, Codex, Cursor, and Gemini. Missing -> added: - site/content/reference/hooks.md: the public /docs/hooks page (tagline, the post-edit list, and the manifest table) now covers GitHub Copilot, including the `.github/hooks/impeccable.json` surface and the default-branch/trust note. (Only skill/reference/hooks.md was updated in the feature PR; this is the website doc.) - README.md Design hook section + the manifest surface list. - site/content/tutorials/getting-started.md hook note. - site/pages/faq.astro tool-specific setup list and the docs-links list. - PRODUCT.md audience line and README.npm.md suite description. Prominence + naming: - README "Supported Tools" and the homepage hero logo row: move GitHub Copilot up to third (after Claude Code) instead of trailing. - site/pages/designing: list GitHub Copilot earlier, full name. - README "Supported Tools": the harness link now points at GitHub Copilot (github.com/features/copilot) instead of the unrelated VS Code entry. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
51d01e3a5f |
[codex] Add design-aware detector rules (#252)
* Add design-aware detector rules * Fix design-aware detector noise * Unify CLI and hook detector ignores * Fix remaining design-system review findings * Add detector ignore CLI * Fix design detector review findings * Fix design color source false positives * Fix core test suite registration * Add design-aware detector docs * Fix font priority design-system parsing * Fix color ignore value matching |
||
|
|
32c01595e2 | Prepare CLI 3.0.1 install targeting fix | ||
|
|
672517f76e |
Add automatic design hook install and exceptions (#170)
* docs: add PRD for design detector hook integration Plans a PostToolUse hook for Claude Code and Codex that runs the existing design detector after every relevant file write and feeds findings back to the agent as advisory system-reminder context. No implementation in this commit; covers UX, technical design, build pipeline changes, distribution, coverage tradeoffs, and rollout. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: revise hook PRD with best-practices review Folds in the P0/P1/P2 findings from an online best-practices critique against the official Claude Code and Codex hook references plus 10+ 2026 community guides and similar prior-art tools (claw-hooks, claude-code-hooks-mastery). Key changes: - Exec form everywhere (Codex snippet was shell form), with Windows rationale. - Default timeout dropped from 10s to 5s. - Re-entrancy guard (CLAUDE_HOOK_DEPTH) and per-file edit counter. - Session-scoped finding dedup promoted from open question to v1. - Per-language inline-ignore syntax map (HTML/JSX/CSS/JS). - Hard-skip rules for sensitive paths and generated/lock files. - Honest framing about Claude Code lacking per-plugin hook disable. - Honest framing about Bash-written files being invisible in v1. - Codex Windows-not-supported call-out, feature flag note, trust ceremony detail. - Optional NDJSON audit log via IMPECCABLE_HOOK_LOG. - Findings cap lowered 8 → 5 with attention-budget rationale. - Versioned envelope ([impeccable@1]) on rendered template. - Expanded test plan, decision log, and stdin payload appendix. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(hooks): ship the design detector hook for Claude Code and Codex Implements docs/hooks-prd.md: a PostToolUse hook that runs the impeccable design detector after every Edit/Write/MultiEdit on a UI file and pushes findings into the agent's next-turn context as a short system reminder. Silent on clean files. Never blocks an edit. Why this matters: today, design slop (side-tab borders, gradient text, purple/cyan palettes, bounce easing, etc.) only gets caught when a human notices or someone explicitly runs /impeccable audit. The hook closes the loop at the moment slop is written. What ships in v1 - skill/scripts/hook.mjs: PostToolUse entry. Reads stdin, runs the detector in-process (no `npx impeccable` cold start), emits hookSpecificOutput.additionalContext when fresh findings exist. - skill/scripts/hook-lib.mjs: extracted helpers (config, cache, filter, render, audit log, runHook orchestrator). 100% unit-testable. - skill/scripts/hook-session-start.mjs: SessionStart greeting, gated by a project-scannable probe + 30-day throttle. - skill/scripts/hook-admin.mjs: backs /impeccable hooks on/off/status/ignore-rule/ignore-file/reset. Hardening built in - Re-entrancy guard (IMPECCABLE_HOOK_DEPTH) so the hook can never recursively spawn itself. - Hard-skip regexes for sensitive paths (.env, .pem, id_rsa, secrets, credentials, .git) and generated/lock/build output. These fire before the file is even read; cannot be turned off via config. - Path-traversal check on the inbound file_path. - Session-scoped dedup keyed by (session, file, rule, line) so the same finding never lands in context twice. Prevents the ~12.5K wasted tokens per chatty session called out in the PRD. - Per-(session, file) edit counter with a one-shot suppression notice on the 7th edit, silent after. - Fail-open contract: every error path returns exit 0 with no stdout. Optional NDJSON audit log via IMPECCABLE_HOOK_LOG. Three kill switches (precedence high to low): 1. IMPECCABLE_HOOK_DISABLED env var (1/true/yes/on, case-insensitive) 2. .impeccable/hook.json `enabled: false` 3. /impeccable hooks off slash command (writes the JSON) Inline ignores are language-aware. `// impeccable: ignore <rule>` for JS/TS, `<!-- impeccable: ignore <rule> -->` for HTML/Vue/Svelte/Astro, `{/* impeccable: ignore <rule> */}` for JSX/TSX, `/* impeccable: ignore <rule> */` for CSS. `*` matches any rule. Directive applies to the next non-blank line. Same shape as ESLint, Stylelint, Biome. Build pipeline - scripts/lib/transformers/hooks.js: per-provider hooks.json builders, plus the slim .codex-plugin/plugin.json manifest. - providers.js: emitHooks: 'claude' for claude-code, emitHooks: 'codex' for codex and agents. Codex also emits emitCodexPlugin. - factory.js: emits hooks/hooks.json next to the skills tree. - build.js: syncs hooks/ into harness roots and into the slim plugin/ subtree; writes .codex-plugin/plugin.json. Build is idempotent (verified: 98 staged files unchanged across two runs). Claude Code wiring uses exec form (command + args) and the ${CLAUDE_PLUGIN_ROOT} placeholder. Matcher: Edit|Write|MultiEdit. `if:` glob filters to UI extensions before spawning Node. PostToolUse timeout 5s, SessionStart timeout 3s. Codex wiring uses ${PLUGIN_ROOT} (Codex's native placeholder), matcher Edit|Write|apply_patch, no `if:` analog (the script does the extension filter). macOS and Linux only; hooks are disabled on Windows in current Codex builds. The trust ceremony and feature flag are documented in README.md. Routing - /impeccable hooks lives outside the 23-command router table on purpose: it is plumbing, not a design skill. The hidden routing slot is added to SKILL.md alongside pin/unpin so the LLM knows to dispatch it. The 23-command count and all stale-count validators remain happy. Tests - tests/hook.test.mjs: 38 unit tests covering env parsing, config load + defaults + malformed, cache round-trip + GC, ignoreRules/minSeverity/inline ignores (all four languages), globbing with **/*/{a,b}, render template with cap + clamp + 0-line prefix drop, audit log NDJSON, payload event-name parameterization, re-entrancy, kill switches, sensitive-path + generated-path + traversal skips, allowlist filter, config ignoreFiles, edit counter cycle including the 7th-edit notice, MultiEdit and apply_patch payload shapes, detector throw swallow, malformed stdin, missing file race. - tests/hook-build.test.mjs: 18 integration tests covering hook manifest shape (matcher, timeouts, exec form, if: glob, placeholders), Codex differences (${PLUGIN_ROOT}, no if:, no SessionStart), Codex plugin manifest (no inline hooks field to avoid the duplicate-file error), routing across the hooksJsonFor table, and presence of all three committed artifacts plus the bundled detector the runtime relative-import path depends on. Full suite: 175 bun tests + 186 node tests, all green. Docs - README.md: new "Design hook" section explaining default behavior, per-project / global / inline disable paths, the JSON schema knobs, the audit log debug flag, and the slop / a11y coverage split. - HARNESSES.md: flips the `hooks` row for Codex from No -> Yes (Claude was already Yes), adds a per-harness hook-surface table with the manifest location and matcher each provider uses. Open questions from the PRD intentionally deferred to v2: Bash-write blind spot, effort-aware suppression, Stop-hook session summary, per-rule severity, async hook mode. None block v1. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix Codex hook scanning: apply_patch paths and co-located stylesheets Parse file targets from Codex apply_patch command bodies, co-scan imported and sibling CSS when UI components are edited, drop the git-sweep PostToolUse group, and align Codex SessionStart manifest and trust docs with the official hooks spec. Co-authored-by: Cursor <cursoragent@cursor.com> * Gitignore hook session cache and drop local test HTML Hook dedup/throttle state in .impeccable/hook.cache.json is per-project runtime data like other .impeccable/ sidecars. Remove an untracked bad-nested-flexbox scratch page from site/public/. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix Claude Code hook: drop Edit-only if filter so Write/MultiEdit fire Claude's if permission rule binds to one tool name, so Edit(*.{…}) never spawned the hook on Write or MultiEdit despite the matcher listing them. Extension filtering now lives in hook-lib on both Claude and Codex. Co-authored-by: Cursor <cursoragent@cursor.com> * Surface Cursor design findings via stop-hook followup Replace dropped postToolUse additional_context with afterFileEdit recording and a one-shot stop followup_message so anti-pattern nudges reach the agent. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix design hook packaging and scans * Fix Cursor hook pending bucket fallback * Fix Sass hook scan coverage * Fix Cursor hook review findings * Fix session start dead hook normalization * Fix hook config and relative scan paths * Remove SessionStart design hook * Remove redundant afterFileEdit normalization * Fix Cursor suppression and module style scans * Fix sensitive path hook filter * Fix disabled Cursor stop hook emission * Refresh hook harness artifacts * Fix Cursor hook manifest install * Add hook ignore-value support * Ignore hook runtime files locally * Fix Codex plugin hook packaging * fix: address PR review bot findings Block numeric hook depth counters from re-entering. Avoid following stylesheet imports from traversal-looking hook targets. * fix: gate ignore-value suggestions by supported rules Only render exact ignore-value commands when the same finding can be suppressed by ignoreValues. * Package Codex plugin as hook-only * Remove Codex plugin packaging * Recover hook install probe plumbing * Remove Codex hook packaging follow-up doc * Remove extra hook docs and skill wording changes * Install real design hooks via skills CLI * Add provider hook smoke runner * Fix Cursor hook delivery with preToolUse gate * Simplify Cursor hook install to preToolUse * Clarify confirmed hook exceptions * Persist hook ignores in shared config * Guard font hook exceptions * Fix hook install after main rebase * Fix hook scan target handling * fix: address hook review findings * Address hook review feedback * Stabilize DeepSeek insert live fixture * Fix Cursor hook Python shell write bypass --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
b4e4fe1079 | Improve docs starter experience | ||
|
|
1aedbcf538 | Add Git submodule skill linking | ||
|
|
75fc95947e | Bump Node support to 24 | ||
|
|
444e4acad3 |
Detector: add italic-serif display headline + hero eyebrow chip rules (#127) (#129)
* feat(detector): flag italic-serif display heroes and uppercase eyebrow chips (#127) Two new rules covering the structural tells of late-2025/early-2026 AI-generated marketing pages. - italic-serif-display: oversized italic serif (Fraunces, Recoleta, Newsreader, Playfair, Cormorant, Tiempos, ...) as the primary hero headline. Anchored on h1 (or h2 at >= 48px) with font-style: italic and a serif primary face. - hero-eyebrow-chip: uppercase letter-spaced label sitting as the previousElementSibling of a hero h1 (font-size >= 48px). Bounded text length 2-30 chars, letter-spacing >= 1.6px, font-size <= 14px. The pill-chip variant (background + border-radius: 999px) falls out of the same gates for free. Both follow the existing icon-tile-stack pattern: pure check function + browser DOM adapter + jsdom adapter, wired into both element loops. Two-column fixtures (4 flag / 6 pass each) drive the jsdom tests. Skill copy in source/skills/impeccable/reference/typography.md and critique.md calls out the patterns by name. The italic-serif rule's description acknowledges that editorial/magazine register may legitimately want the pattern -- judge by context. Closes #127 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add sandbox gotchas for Codex * Trim verbose detector skill copy --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Paul Bakaus <paulbakaus@pauls-mbp-3.lan> |
||
|
|
3c9cc86061 |
Merge CLI into main repo, switch everything to Apache 2.0
Merges the impeccable-detect CLI repo (pbakaus/impeccable-cli@831a6cc) into this repo. The BSL-1.1 license that motivated the split is gone; everything is now Apache 2.0. - Add bin/, src/, detection tests and fixtures from CLI repo - Merge package.json: name → "impeccable", add bin/exports/files fields - Internal refs now read from local src/ instead of node_modules/ - Update SPDX headers, NOTICE.md, CLAUDE.md, FAQ, npm README - Add prepack/postpack scripts for CLI-focused README on npm - Remove terminal license labels (no longer needed) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
d8d5b8acd8 |
Move CLI to separate repo, make this skills-only (Apache 2.0)
The CLI and detection engine now live in pbakaus/impeccable-detect (published as 'impeccable' on npm, BSL-1.1). This repo is purely Apache 2.0: skills, prompts, website, and build system. - Remove bin/ (CLI moved to CLI repo) - Remove README.npm.md (moved to CLI repo) - Remove @impeccable/detect dependency, add impeccable dependency - Set package.json to private (no longer published to npm) - Update all references from @impeccable/detect to impeccable - Update CLAUDE.md, NOTICE.md, FAQ, and changelog - Rebuild all provider skill distributions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
da8a59e981 |
Add skills subcommand and fix npm metadata
New `impeccable skills` CLI with three subcommands: - `skills help`: fetches and displays all 20 commands from the API - `skills install`: delegates to `npx skills add pbakaus/impeccable` - `skills update`: tries `npx skills update` first; if skills aren't managed by the skills CLI, downloads the universal bundle from impeccable.style and overwrites provider folders directly, with git-based modification detection and confirmation prompt Also fixes npm metadata: homepage -> impeccable.style, license -> Apache-2.0 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
8d0e9de26d |
Prepare CLI for npm: v2.0.1, Node compat, npm-specific README
- Rename bin/impeccable.mjs to bin/impeccable (npm rejects .mjs in bin) - Shebang: #!/usr/bin/env node (works without Bun) - Add README.npm.md with CLI-focused docs, swapped in during publish - Build browser script to source/ dir so URL scanning works in npm pkg - Include browser script in files field - Move website-only deps (archiver, motion, playwright) to devDependencies - jsdom as dependency, puppeteer as optionalDependency - Bump version to 2.0.1 across package.json, plugin.json, marketplace.json Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |