* Add inline, in-file ignore comments for the detector (issue #283)
Complement config ignores with eslint-disable-style waivers that live where
they apply and travel with the file when it leaves the repo. The motivating
case is a generated/exported standalone document that legitimately uses a
first-party brand typeface (on the overused-font list) and is later scanned
without .impeccable/config.json present.
Marker is comment-syntax-agnostic (works in //, /* */, <!-- -->, #, {/* */}):
impeccable-disable <rule>[, <rule>...] [-- reason | : reason] whole file
impeccable-disable-line <rule>... same line
impeccable-disable-next-line <rule>... next line
Bare directive or * means every rule; reason is optional and discarded at
scan time. Behavior is suppression, for parity with config ignores.
Implementation:
- New pure module cli/engine/shared/inline-ignores.mjs (parser + filter, no
Node deps). Static-HTML findings have no line number, so only whole-file
directives apply there -- exactly the standalone-document case; the
regex/text engine additionally honors the line-scoped forms.
- Wired into detectText and detectHtml, gated by options.inlineIgnores.
- detect CLI applies inline ignores by default; --no-inline-ignores skips
just them, --no-config skips config and inline ignores together.
Docs: config.md (new section), detector.md, README. skill/reference/hooks.md
reversed its prior "inline comments are not supported" guidance and now points
the agent to inline waivers for the travels-with-the-file case. Changelog 3.x.
Tests: tests/inline-ignores.test.mjs (parser units, detectText/detectHtml
integration, CLI end-to-end), registered in the detector suite.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Reconcile design hook wording with inline ignores
Two hook-side fixes prompted by review of the new inline-ignore feature:
1. Clean-ack steer line. The old line ("Keep typography hierarchy, spacing
rhythm, and color contrast intentional on the next change.") read as an
odd non-sequitur after "No anti-patterns." Reworded the whole clean ack to
say what it means: a clean scan only clears the deterministic rule set, not
overall design quality, so keep following the design system and skill
guidance. Now: "Design hook scanned X. No deterministic design-quality
issues found. That does not mean the design is good: keep following the
project design system and the impeccable skill guidance."
2. Directive footer. It still told the agent "Do not add source comments such
as `impeccable: ignore`; those pollute the code and do not suppress hook
findings." That is now misleading: the hook runs the same detector engine
as the CLI, which honors inline `impeccable-disable` waivers, so they DO
suppress hook findings (consistent with config ignores, which filterFindings
already honors). Reworded to: don't silence a real finding to skip fixing
it; suppress only after the user confirms intent; prefer a config ignore,
and reach for an inline `impeccable-disable <rule>` comment only when the
waiver must travel with a file that leaves the repo.
Added a hook test asserting an inline `impeccable-disable-line` comment makes
the hook scan the file clean (locks in the cross-cutting behavior), and updated
the clean-ack / footer assertions to the new wording.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Address review on inline-ignores parser
- Case-insensitive fast-path bail-out (Cursor): the cheap substring guard was
lowercase-only while DIRECTIVE_RE has the `i` flag, so a mixed-case marker
like `Impeccable-Disable` skipped parsing entirely and never suppressed.
Switched the guard to `/impeccable-disable/i.test(...)`. Added a regression
test.
- Removed the unreachable `-->` branch from TRAILING_CLOSER_RE (Greptile):
`--+>` already matches `-->` and any longer dash run.
- Replaced the always-truthy lazy-match + `if (sep)` reason strip with an
explicit first-separator slice (Greptile): clearer and drops the dead branch.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Align inline-ignore line numbering with the detector (CRLF/CR endings)
parseInlineIgnores split lines with /\r\n|\r|\n/, but detectText numbers lines
with split('\n'). On classic `\r`-only endings the two diverged, so a
disable-line / disable-next-line directive could key a different line than the
finding it should waive (Cursor review). Split on '\n' only, matching the
detector exactly; the directive regex already excludes '\r', so a trailing '\r'
on CRLF files is never captured into the rule list. Added a CRLF regression test
through the real detectText.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two Cursor Bugbot Medium findings on the merged monorepo context PR:
- Excluded packages still listed: discoverTargetCandidates added every glob
match but never applied negated workspace patterns, so an excluded package
(e.g. "!packages/internal") showed up as a selectable target even though
resolveWorkspaceProjectRoot sends it back to the repo root. Now filtered
with the same isExcludedByWorkspacePattern check the resolver uses.
- Empty app list blocks root: resolveTargetSelection returned
TARGET_SELECTION_REQUIRED whenever projectRoot === repoRoot, even with zero
discoverable child apps (e.g. `workspaces: ["."]`), leaving an unanswerable
prompt. It now returns null (use the repo root as the project) when there
are no candidates.
Also documents two Greptile P2 clarity notes (the four contextSourceStatus
labels incl. the dual meaning of 'fallback', and the deliberate
isMonorepoRoot-before-hasGitBoundary ordering in findMonorepoRoot).
Adds regression tests for both behaviors.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
* Add GitHub Copilot hook support (CLI + cloud agent)
Wire the Impeccable design detector into GitHub Copilot's hook system so
direct file edits get the same post-edit design feedback the Claude Code,
Codex, and Cursor harnesses already receive.
GitHub Copilot's contract differs from the existing harnesses (verified
against Copilot CLI 1.0.63):
- Repo-level manifest at `.github/hooks/impeccable.json` (read by both the
CLI, once committed to the default branch, and the cloud/app agent).
- Flat `postToolUse` entries with `bash`/`timeoutSec` and a full-match
`matcher` regex; the file-editing tools are `edit` and `create`.
- The stdin event uses camelCase `toolName`/`toolArgs`, where `toolArgs` is
a JSON *string* carrying the touched file under `path`.
- Context is injected via a top-level `additionalContext` string.
Changes:
- hooks.js: buildGitHubHooksManifest() + route `github` in hooksJsonFor().
- providers.js: emitHooks/hooksManifestRel for the github provider.
- hook-lib.mjs: detect the github harness, normalize the camelCase event
(parse the JSON-string toolArgs -> tool_input.file_path), and emit the
`additionalContext` payload shape.
- hook-admin.mjs / skills.mjs: install + idempotent-repair the
`.github/hooks/impeccable.json` manifest (bash-aware marker stripping).
- hooks.md: document GitHub Copilot as a supported harness.
- Tests for the builder, routing, event normalization, and end-to-end run.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Cover Copilot apply_patch edits in the hook (live-verified)
The first cut matched only `edit|create`, the tool names `copilot -p` uses.
A live trace against Copilot CLI 1.0.63 in an interactive session showed it
edits files via `apply_patch`, whose toolArgs is a raw OpenAI-format patch
string (`*** Begin Patch` / `*** Add File:`), not JSON. With the narrow
matcher the hook command never ran.
- hooks.js / hook-admin.mjs: matcher -> `edit|create|apply_patch`.
- hook-lib.mjs: normalizeGitHubEvent now routes apply_patch's raw patch
string into tool_input.command (reusing the existing parseApplyPatchPaths /
resolveTargetFiles plumbing) and only JSON-parses toolArgs for the
edit/create/view tools. tool_name is normalized to apply_patch so the patch
path is extracted even if a future build relabels the tool.
- Tests: apply_patch matcher assertions, event normalization, and an
end-to-end runHook covering the interactive/cloud path.
Verified live: a trusted interactive `apply_patch` edit fires the hook and
returns the expected `additionalContext` design reminder.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Address review feedback + add changelog entry
- hook-lib.mjs (Bugbot, low): looksLikeApplyPatch no longer misroutes an
edit/create event whose edited *content* contains apply_patch markers. A
real apply_patch payload is a raw string that does not parse as JSON; an
edit payload is a JSON object, so only non-JSON-object strings are treated
as apply_patch. Edit events keep extracting `path`. Adds a regression test.
- skills.mjs (Bugbot, medium): document why `.github` is intentionally
excluded from hookScriptPathForProvider. Its hook manifest is committed and
shared (read by the Copilot cloud agent and teammates), so the command must
stay portable via `$(git rev-parse ...)`; rewriting it to a machine-local
absolute path would break those. GitHub skills are project-scoped, so the
project-relative path resolves.
- changelog: add an Upcoming (v3.x placeholder) entry for the Copilot hook.
Version is not bumped yet (batching with other changes).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Guard plugin/skill version drift in the build (issue #274)
The Claude Code marketplace installs from the committed ./plugin subtree,
so a version disagreement between the hand-edited manifests and the
generated subtree ships stale content under a wrong version. This is the
class of bug reported in #274: a version bump that doesn't regenerate
./plugin (e.g. PR #252, where root plugin.json was 3.7.0 while
plugin/.claude-plugin/plugin.json was still 3.6.0) merges a drift window
onto main, and marketplace/Cowork installs pull the stale subtree.
Add a build-time validator that treats root .claude-plugin/plugin.json
as the source of truth and fails the build if any of these disagree:
- .claude-plugin/marketplace.json plugins[0].version (hand-edited; the
post-merge sync workflow never bumps versions, so it can't repair a
mismatch here)
- plugin/.claude-plugin/plugin.json version (generated subtree)
- plugin/skills/impeccable/SKILL.md frontmatter version (bundled skill)
It only fires on an inconsistent bump; PRs that don't touch versions keep
every file in agreement and stay silent. The pure comparison lives in
scripts/lib/validate-plugin-versions.js with direct unit coverage; build.js
owns the logging and the non-zero exit. Documents the regenerate-on-bump
step in CLAUDE.md's Versioning section.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Harden version-drift collector against malformed/incomplete manifests
Address Greptile review on #278:
- Wrap every file read/parse in a sentinel helper (extractFromFile) so a
half-edited manifest — the exact state during a version bump — yields a
clean "could not parse (...)" diagnostic naming the file instead of a raw
JSON.parse stack trace out of build().
- Report a present-but-malformed root plugin.json, or one missing its
`version` field, as an explicit error. Previously `undefined` version
short-circuited the build wrapper's `source == null` guard and passed
silently. collectPluginVersions now returns an `errors` array; build.js
fails on errors + mismatches combined, and only the genuinely-absent root
manifest is a no-op skip.
Adds 4 unit tests: malformed checked manifest, malformed root, missing
version field, and the absent-root no-errors case.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Make SKILL.md frontmatter version read CRLF-tolerant
Address Cursor Bugbot review on #278: readSkillFrontmatterVersion only
matched `\n` delimiters, while the shared parseFrontmatter in
scripts/lib/utils.js accepts `\r?\n`. A bundled SKILL.md saved with CRLF
line endings would parse to a null version and trip a false mismatch
against root plugin.json even when the version line is correct.
Match the shared parser's `\r?\n` tolerance and drop the `$` anchor on
the version line (it would not match before a `\r`). Adds CRLF coverage
for both readSkillFrontmatterVersion and collectPluginVersions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Re-trigger CI (no file change)
CI did not fire for 5cda9f6b; force a fresh run on the current tree.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Fix React hydration mismatch from live scroll-lock on SSR roots
The live overlay's startScrollLock disabled the browser's scroll
anchoring by setting `overflow-anchor: none` as an inline style on
`<html>` and `<body>`. On frameworks that server-render those roots
(notably Next.js App Router), that client-only inline style desyncs from
the server HTML, so React 19 logs "a tree hydrated but some attributes
of the server rendered HTML didn't match" on the next Fast-Refresh
re-render. It surfaced as a flaky failure of the nextjs-app-router
live-e2e fixture's expectConsoleClean probe.
Inject the suppression as a `<style>` rule keyed by a stable id instead
of mutating inline styles on hydrated host elements. Same computed
effect, but React no longer sees a client-only attribute on `<html>` /
`<body>`. The rule is recreated on every startScrollLock and removed on
teardown, so reload survival (driven by the persisted scroll key) is
unchanged.
Adds a regression guard pinning the new shape (no inline overflowAnchor
mutation on html/body; injected <style> created and removed by id).
Verified end-to-end: the nextjs-app-router live-e2e fixture now passes
the expectConsoleClean probe deterministically.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Relax regression-guard regex spans to {0,400}
Address Greptile review: the {0,200}/{0,220}/{0,160} character-span
limits between the injected-style constructs were tight enough that an
innocent refactor or added comment inside startScrollLock could silently
break the shape-check. Widen each segment to {0,400}; the guard still
passes on the fix and still fails when the inline html/body overflowAnchor
mutation is reintroduced.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Replace npx hints in live scripts with bundled-script paths
The live-mode poll/wrap scripts are invoked by the agent via
`node {{scripts_path}}/live-*.mjs`, never through the `npx impeccable`
CLI. Their help text and runtime error hints still pointed at
`npx impeccable poll|live|wrap`, which is misleading and, for the
error paths, not directly runnable.
- Docstrings/comments (never executed): switch to the
`node <scripts_path>/...` convention already used by live-server.mjs.
- Runtime-printed error/usage strings: resolve the script's own dir via
import.meta.url and print a real, copy-pasteable absolute path instead
of a placeholder.
Verified by triggering the error paths from the synced bundle and by
running the live-mode E2E (vite8-react-modal) through the full cycle.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Quote script paths in runtime hints to handle spaces
Paths containing spaces (e.g. /Users/john doe/...) would otherwise
produce a non-runnable command. Addresses Greptile review feedback.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The bundled detector's cli/main.mjs imports ../../lib/impeccable-config.mjs,
which in the source CLI resolves to cli/lib/impeccable-config.mjs. The skill
build only copies cli/engine/** into scripts/detector/**, leaving that
dependency behind, so from the bundled scripts/detector/cli/main.mjs the same
import resolved to scripts/lib/impeccable-config.mjs and failed with
"Cannot find module .../lib/impeccable-config.mjs". /impeccable critique (and
any detector-backed command) crashed on startup for every provider since #252.
Teach the detector bundler to copy out-of-bundle engine dependencies into the
skill's scripts/lib/, and add a build test that walks every bundled script and
asserts each relative import resolves to another bundled file, so a future
out-of-bundle dependency fails the test instead of the user.
Skill v3.7.1 (patch). CLI unchanged — the engine resolves fine in the CLI's
own tree; only the skill bundling was wrong.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On Node v24.16.0 / v26.1.0+, `impeccable install` printed "Downloading
impeccable skills...", exited 0, and installed nothing. A Node streams
regression (nodejs/node#63487) made pause()/resume() no-ops on destroyed
streams, stalling extract-zip's yauzl/fd-slicer read stack partway through;
its promise never settled and the process exited clean with no error.
Swap extract-zip for fflate across both extraction call sites
(downloadAndExtractBundle, copyOrExtractLocalBundle) via a new extractZip
helper. fflate decompresses from an in-memory buffer and never touches the
fs stream path, so it is immune on every Node version. It is pure JS with
zero dependencies, so the Windows fix from #198 (no `unzip` binary) holds.
Unlike extract-zip, fflate is actively maintained.
Because extractZip writes entries itself, it guards against zip-slip (`../`
entries escaping the target dir). Tests add a many-file regression guard
(fails on partial extraction) and a zip-slip rejection test.
Verified end-to-end: the real 1,194-file universal bundle extracts and
installs completely.
Fixes#250.
Co-authored-by: Claude <noreply@anthropic.com>
* feat(cli): interactive hook consent + unified .impeccable/config.json
Make the design-hook install a conscious choice and unify scattered config
into one file.
Interactive consent
- On an interactive `skills install`/`update`, the CLI explains what the hook
does and offers to install it (default yes), then records the per-developer
decision in the gitignored `.impeccable/config.local.json`, so it never
re-asks. A recorded decision or an already-installed hook short-circuits;
`-y`/non-TTY keeps the historical install-by-default behavior; `--no-hooks`
is a one-off skip that records nothing. The trigger keys on "is the hook
installed?" + "is there a recorded decision?", not a brittle version check.
Unified config
- `.impeccable/config.json` (shared) and `.impeccable/config.local.json`
(gitignored) now hold all Impeccable settings: hook settings under a `hook`
key, plus top-level `updateCheck`. `/impeccable hooks` writes the `hook`
subtree, preserving siblings. The hook runtime reads `hook.quiet` and
`hook.auditLog`; context boot reads `updateCheck`. The legacy
`IMPECCABLE_HOOK_DISABLED|QUIET|LOG` and `IMPECCABLE_NO_UPDATE_CHECK` env vars
still work and override config; docs now lead with config and treat env vars
as a legacy note.
- No backward compat for the pre-unification `hook.json`/`hook.local.json`
(the hook shipped an hour ago; nothing in the wild uses it). This repo's own
hook config is migrated to `.impeccable/config.json`.
The CLI and skill scripts are separate trees, so a small CLI-side config module
(cli/lib/impeccable-config.mjs) duplicates the config-path and .git/info/exclude
handling; comments flag the duplication.
Tests: new cli config unit test; skills-cli consent tests (declined skips,
accepted installs, --no-hooks records nothing); hook.test.mjs back-compat
removed and quiet/auditLog-from-config + gitexclude coverage added. Full suite
green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(hooks): preserve sibling config fields + resolve audit log from event cwd (Bugbot)
Two Bugbot findings:
- High: `/impeccable hooks` edits replaced the whole `hook` object with the
merge-helper output, dropping fields those helpers don't manage — so an
`ignore-value --local` could wipe the recorded install consent and make the
CLI re-prompt. writeConfig now merges over the existing hook object, keeping
consent/quiet/auditLog.
- Medium: config-based audit logging resolved hook.auditLog from process.cwd(),
which can differ from the hook event's project root (and Cursor's pre-edit
hook passed no cwd). The hook now stamps the resolved project root on the
audit entry, and writeAuditLog reads config from entry.cwd when present.
Tests: a /impeccable hooks edit preserves consent + quiet; writeAuditLog
resolves config auditLog from entry.cwd, not the fallback cwd.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(hooks): resolve a relative auditLog path against the project root (Bugbot)
A relative hook.auditLog was read from the project root but written relative to
the hook process cwd, so when those differ the log went to the wrong place.
writeAuditLog now resolves a relative target (from env or config) against the
same project root it reads config from. Absolute and ~/ paths are unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Fix hook consent recovery and smoke config
* Fix hook consent explainer for Cursor
* Fix empty hook target consent
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(hooks): package design hook in plugin, install to settings.local.json
Three related changes to how the Impeccable design hook is distributed,
plus an unrelated build fix discovered along the way.
Package the hook in the Claude Code plugin
- The marketplace / `/plugin install` path previously shipped the skill and
agents but no hook, so those users never got the design detector. The build
now emits `plugin/hooks/hooks.json` (auto-discovered at the plugin root),
resolving the script via `${CLAUDE_PLUGIN_ROOT}` so it works wherever Claude
Code unpacks the plugin instead of assuming a `.claude/skills/` layout.
CLI installs the hook into settings.local.json, not shared settings.json
- `npx impeccable skills install/update` now writes the Claude hook to the
gitignored `.claude/settings.local.json` (a machine-local install side
effect) rather than the team-shared `settings.json`, which could otherwise
be committed and break for teammates without the skill installed.
- Graceful handling (leave-it-never-duplicate): if our hook already lives in
the shared `settings.json` (a legacy install or a deliberate user move), it
is honored in place and never duplicated into the local override, which
would otherwise run the detector twice per edit.
- The skill's `/impeccable hooks on|off` toggle is unaffected: it only writes
`.impeccable/hook.json`, never the settings files.
Fix universal.zip build failure under archiver v8
- `archiver` was bumped to v8 (now ESM, factory function removed) but
`scripts/lib/zip.js` still used the old `archiver('zip', ...)` API, so every
build silently failed to produce `dist/universal.zip` (the skill-release
artifact). Switched to `new ZipArchive({...})`.
Also folds in a pre-existing local rename of the hook status message
("Scanning design" -> "Checking UI changes") and its regenerated provider
output.
Tests: new coverage for the plugin-packaged hook manifest and the
shared-settings honor-in-place path; existing CLI assertions moved to
settings.local.json. Full suite green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(cli): detect hook by marker, not file existence (Bugbot)
hookInstalledForProvider treated any existing settings.local.json (or
hooks.json) as proof the hook was installed. Those files commonly hold
unrelated local settings, so the already-installed `skills install` path
would skip repairing a genuinely missing hook that `update` would add.
Detect the Impeccable marker in the file instead of mere existence. Adds a
test for the exact case: a settings.local.json with only permissions still
triggers hook repair and preserves the unrelated content.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(build): fail loud on a broken release zip + cover the zip writer
Close the gap that let the archiver v8 break ship a 0-byte universal.zip
with a green test suite:
- createProviderZip no longer swallows failures. It throws on a missing
source, an archive with zero entries, or a 0-byte output, and build() now
exits non-zero on any such rejection. A build that can't produce its release
artifact fails instead of deploying an empty bundle.
- New tests/zip.test.mjs exercises the real zip writer and round-trips through
extract-zip (the unpacker the CLI uses): a valid bundle unpacks to the skill
tree, and the empty/missing-source cases throw. Wired into the core suite so
it runs in `bun run test`.
Why this matters: the prior CLI e2e tests stub the bundle as a local
directory, so they never built, downloaded, or unzipped a real archive. The
zip writer had no coverage and failed soft, so Dependabot's archiver 7->8
major bump merged green and the deploy shipped an unusable bundle.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(cli): scope hook marker scan to the hooks subtree + prune local dupes (Bugbot)
Two follow-ups from Bugbot:
- fileHasImpeccableHookMarker scanned the whole settings file as raw text, so
an unrelated string (e.g. a permissions allow entry that mentions the hook
path) could falsely read as an installed hook and block install/repair or
the shared-settings skip. Now it parses the JSON and scans only the `hooks`
subtree.
- When the hook is honored in the shared settings.json, copyProviderHooks
skipped the local write but left a stale hook in settings.local.json from an
earlier machine-local install, so Claude Code loaded both and ran the
detector twice per edit. It now prunes the local copy (preserving unrelated
local settings, dropping the file if only our scaffolding remained).
Adds tests for both: a permissions string mentioning the hook path still
triggers repair, and a shared hook prunes the stale local duplicate while
keeping unrelated permissions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Fix: tear down annotation overlay when Escape exits live pick mode.
The configure prompt auto-focuses and bypasses the global Escape handler, so its local path must hide the annot overlay; togglePick off now does the same as a safety net.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Improve live mode steer pill typing affordance.
Show a visible caret and placeholder when focused, expand on pointerdown, and drop the muddy border so the graphite surface carries the affordance alone.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Improve live mode configure bar layout and pill styling.
Align pills and input on a shared text track, refine muted pill chrome with a quiet action border, and center the row with symmetric inset so spacing reads evenly in the 36px bar.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add x1 to live mode variant count picker.
The configure bar count pill now cycles 1→2→3→4→1 so users can request a single variant.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Polish live mode configure bar, edit badge, and action picker.
Refine selection pill layout and tooltips, shrink edit copy to an icon aligned with the outline, right-align the action picker, and sync demo styles and regression coverage.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix live mode element nav when configure input is focused.
Passthrough empty arrow keys from the configure and steer prompts so handleKeyDown can move between pickable elements without breaking autofocus typing.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Remove accidental live.js inject from Base.astro.
Strip the localhost helper script tag left over from local live mode iteration so the PR ships only intentional UI changes.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix review findings: pick-cursor state sync, anchor recovery, e2e selectors.
Code review of this branch surfaced ten confirmed bugs plus three smaller
ones; this commit fixes all of them.
- Route every interaction-state transition through a new setLiveState()
helper that re-syncs the pick-mode crosshair, fixing four confirmed
cursor bugs: never appearing on pick toggle (sync ran before the state
change), sticking through the configure phase, surviving teardown
page-wide, and the style mounting inside the adapter's shadow root
where it can't match the host document (now document.head).
- Anchor recovery: a matching id is decisive again (hashed class names
and component tags broke recovery), empty-text elements can no longer
match the fuzzy text passes (".includes('')" hole plus shortest-text
preference), and the dead 2-class-subset fallback is removed.
- Selection pill: drop the hover-only "armed" guard so keyboard
activation works; the pill arms on focus as well as hover.
- Configure chrome: remove the configure-bar tooltip on teardown, align
restorePickerBarChrome padding with initBar (5px), share the
configure-input stylesheet with the insert row, and sync the
ui-core.mjs surface inventory with live-browser.js.
- Site demos: delete the stale duplicate .live-demo-ctx-selection rule
that killed the teal pill on dark pages, and keep the configure-phase
demo bar on the overlay's dark surface in light mode so the near-white
prompt text stays readable.
- E2E/contract tests: match the icon-only submit button by aria-label
("Generate variants") instead of the removed "Go" text, and update
source-contract pins for setLiveState and buildConfigureSubmitButton.
Verified: bun run test green, live-mode E2E 23/23 across all fixtures.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Wire insert voice button into syncVoiceUi listening state.
Voice on the insert configure row runs through the same 'configure' mode,
but syncVoiceUi only stamped data-listening/aria state on the replace
row's #impeccable-live-configure-voice, so the insert button never pulsed
while listening. Target whichever of the two row buttons is mounted, the
same either-row pattern syncConfigureInputChrome uses.
Addresses Bugbot review comment on PR #242.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Reinject from source when the session wrapper lands during anchor recovery.
The anchor-recovery observer stood down as soon as the session's variant
wrapper appeared in the DOM, without running injectVariantsFromSource.
A wrapper can land incomplete (wrap HMR landed, variant insert did not),
which is exactly the case injectVariantsFromSource's existing-wrapper
replace path handles - so recovery ended with the bar stuck and no
variants. Route both the anchor-found and wrapper-landed cases through
injectVariantsFromSource, which owns wrapper replacement, recovery-flag
clearing, and variant display.
Addresses Bugbot review comment on PR #242.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Restore inline edit drafts before configure chrome teardown disables editing.
teardownConfigureChrome called disableInlineEdit() ahead of hideBar(),
wiping inlineEditRows and the impeccableOriginalText metadata that
hideBar()'s EDITING-state restoreInlineEditDrafts() needs - so turning
Pick off mid "Edit copy" left edited DOM text in place, neither saved
nor canceled. Let hideBar() own the sequence: it restores drafts first,
then disables inline edit.
Addresses Bugbot review comment on PR #242.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Copy guidance (em-dash bans, buzzword bans, button-label / link-text
phrasing, aphoristic-cadence) doesn't belong in the main design skill.
It's not design-specific — the skill is trying to do too much. The six
rules being dropped (every-word-earns, no-em-dashes, no-aphoristic-cadence,
no-buzzwords, button-verb-object, link-standalone) are now better served
by:
- The impeccable engine's antipattern detectors (em-dash-overuse,
marketing-buzzword, aphoristic-cadence, copy-slop) for linting at scan
time.
- The /clarify subcommand for surfacing the same checks when reviewing
copy specifically.
The em-dash ban for the SKILL prose itself still lives in STYLE.md and the
build-time prose validator — that's separate from the skill's guidance to
agents.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The v2.1 ablation sweep (n=10 × 4 brand niches × 3 providers, anchored to
commit 54c3a502, ~544 cells) confirmed these four rules carry no weight in
the skill:
- skill-typo-no-all-caps-body — duplicate of brand-ban-all-caps-body; brand
version is more specific (reserves caps for labels + headings)
- skill-typo-codex-hero-ceiling-repeat — the codex-block restatement of
skill-typo-hero-ceiling didn't add reinforcement on top of the universal
rule
- skill-typo-scale-ratio — duplicate of brand-typo-modular-scale; same
signal, brand version carries the clamp() / fluid implementation detail
- skill-typo-font-count — models don't reach for ≥4 font families in any
niche we test, so the rule has no measurable effect
Each deletion is the Agent A / B / C / D Phase-2 audit recommendation;
none of the four ever validated under either prose state.
Adds EMPIRICAL_VALIDATION.md naming the seven cross-provider winners as the
trustworthy core, and documents the systemic findings (self-priming, detector
saturation, vocabulary anchoring) so future skill edits can avoid the same
traps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the 5 prose changes in skill/SKILL.src.md + skill/reference/brand.md
out to every harness directory (`.claude`, `.gemini`, `.cursor`, `.codex`,
`.agents`, etc.) so the staged skill that workers / agents read matches the
source. Auto-generated by `bun run build:skills`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase-2 ablation audit caught these rules causing the exact behavior they
ban via the literal examples in their own prose. Verified: OpenAI samples
under skill-on produced "fake theater", "vendor theater", "heatmap theater"
as verbatim copies of the 'X theater' example. Same pattern for the
restrained-on-cream example, the aphoristic-cadence template, and the
"reserve uppercase for…" enumeration.
- skill-ban-codex-x-theater: drop the 3 syntactic templates + 3 example
phrases ("Productivity theater" etc.)
- brand-imagery-required: drop the niche enumeration that cued
"imagery not required elsewhere"
- skill-typo-no-all-caps-body: drop the "Reserve uppercase for labels /
eyebrows / badges" enumeration that primed uppercase usage
- brand-color-no-converge: drop the "restrained-on-cream" example that
was priming cream-heavy palettes
- skill-copy-no-aphoristic-cadence: drop the literal cadence template
("serious statement, then punchy short negation") that named the
rhythm it bans
Ablation re-run pending in impeccable-evals to measure impact.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Improve CI test coverage
* Stabilize live E2E harness
* Shard live E2E CI
* Cache live E2E CI dependencies
* Stabilize live E2E smoke CI
* Update generated live browser bundles
* Tighten live E2E smoke runtime
* Prevent live E2E smoke hangs
* Stabilize live E2E CI coverage
* Fix stale accept DOM cleanup
* Regenerate live browser outputs
The verbs/labels/icons were copied three ways: live-browser.js (ICONS + ACTIONS),
VISUAL_ACTIONS in live-event-validation.mjs, and the marketing demo. Collapse
them to one source, skill/scripts/live-vocabulary.mjs (LIVE_COMMANDS + derived
VISUAL_ACTIONS).
- live-event-validation.mjs imports VISUAL_ACTIONS from it.
- live-server.mjs serializes LIVE_COMMANDS into window.__IMPECCABLE_VOCAB__ when
it serves /live.js, next to the token/port. live-browser.js (served raw, can't
import at runtime) builds its ICONS + ACTIONS from that injected vocab instead
of an inline copy — byte-identical icons, zero behaviour change.
- site/components/LiveDemoPalette.astro imports the same module at build time, so
the demo and the real picker can no longer drift.
Adds a /live.js test asserting the injected vocab deep-equals the canonical list.
Harness skill dirs refreshed via build. (Pre-existing, unrelated: `bun run
build:site` fails on an htmlparser2 import in the CLI detector.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rewrite the hero around the why (the missing design vocabulary) instead of the
live-mode how: "The missing design vocabulary for agents." The live demo now
opens the picker's command palette and picks a verb before generating, which is
the move that makes the live approach unique and was previously skipped.
- Demo palette mirrors the real action picker (live-browser.js): same 12 verbs,
the same SVG icons, a 4-col icon-over-label grid, selected chip on a kinpaku
wash with its icon recolored. Light + dark covered.
- Shared <LiveDemoPalette> component renders the grid from one list, so the hero
and /live-mode no longer hand-copy the markup. /live-mode lands on "delight",
the hero on "colorize" (via data-demo-pick); pages without a palette filter the
switcher beats out of the shared timeline.
- Trim the opening beats so the cursor clicks the element at ~1.3s (was ~2s), and
slow the palette browse so the vocabulary is readable.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The changelog + FAQ pages share changelog-faq-kinpaku.css, which paints its
card/panel/pill backgrounds with literal dark oklch(8% …) values that don't
flip with the theme. The only light override was dead twice over: scoped to a
.changelog-faq-kinpaku wrapper that's never on the body (these pages use
.kinpaku-surface), and naming the wrong elements (.faq-item/.changelog-entry
instead of the cf-prefixed classes). So the cards stayed near-black on the
light page.
Replace that dead block with correct html.light .cf-* rules: flip the card
surfaces (cf-entry, cf-stats, cf-faq-item, cf-entry--current), the before/after
frame + before tag (cf-ba-shot, cf-ba-tag--before) to the shared light card
treatment, and drop the bright-kinpaku accents (cf-version, cf-stat-num,
cf-current-badge, cf-ba-tag--after, cf-faq-question) to --ks-kinpaku-ink so
they stay legible on paper, matching the .cf-eyebrow. Answer-body tokens
already flip, so they're untouched. Dark mode unaffected.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each command section now reads in its own category accent (--spread-accent)
across the palette: the kicker, the skill name (big title + active list
item), and the divider all take it, while the slash before /impeccable
drops to the muted namespace ink. The per-section divider accents are also
made vibrant — refine/simplify/harden were muddy kinpaku-pale/-deep/oxide;
now create/refine/simplify = kinpaku gold, evaluate/harden = patina
verdigris, system = neutral, matching the established category color
grouping. Light mode re-applies the accents over the shared eyebrow :is()
rule; dark mode picks them up from the base rules.
Also includes light-mode readability fixes for the live-demo G-bar (brand
mark, active tool chip, control chips, pin-note text/caret) and a
specificity fix so the hotel-hero demo text stays light on its photo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Default is "auto", which inherits from the OS via prefers-color-scheme
and follows it live. Clicking cycles auto → light → dark → auto; the
explicit choice persists in localStorage while auto stores nothing.
The toggle shows the active preference (half-circle / sun / moon) keyed
on a new data-theme-pref attribute, so "auto" is its own visible state
rather than collapsing into whatever the OS resolved to.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The palette's title, active command, and category kicker rendered in the
muddy kinpaku-ink in light mode (the shared eyebrow rule swaps pristine
kinpaku for kinpaku-ink for legibility on warm paper). But the palette
sits on a near-white card, where pristine kinpaku reads fine — and it now
matches the already-pristine slashes and the PALETTE toggle.
Scope the override under .magazine-container so it only affects the
palette and clears the eyebrow rule's :is() specificity (inflated to 4
classes by its .fisheye-item.is-active argument). Other eyebrows keep
kinpaku-ink for paper legibility; dark mode is unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The dark periodic table hardcodes a black stage and near-black tiles
with !important, and the only light override targeted .ptable-cell — a
class the component never emits (it renders .ptable-element). So in light
mode the tiles stayed black while the symbol/name text flipped to dark
ink: black on black, unreadable.
Add a proper light block (with !important to beat the dark rules): a soft
light stage, raised near-white tiles with neutral hairline borders and a
subtle shadow, and a darker hover border. Symbol/name colors and the
category labels already re-theme to dark inks, so they read cleanly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
app.js fetches /_data/api/commands.json and patterns.json, but those are
build artifacts written into site/public/_data/ by scripts/build.js. The
plain `astro dev` server never runs that build, so the homepage 404'd on
both in dev.
Extract generateApiData into scripts/lib/api-data.js (shared by the build
and a new scripts/gen-dev-api.mjs prebuild), and run the prebuild before
astro dev so `bun run dev` serves the same payloads as production.
site/public/_data/ stays gitignored.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
After the border audit reassigned every usage to --ks-kinpaku or
--ks-rule, the muddy gold token is dead. Delete its dark and light
definitions and update the comments that described it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Audit every --ks-rule-strong usage (the muddy desaturated gold). Where
the border/text sits beside pristine kinpaku or signals an active/hover/
brand state, lift it to --ks-kinpaku; where it's a large structural
frame, window chrome, or paired with neutral siblings, drop it to the
neutral --ks-rule hairline.
Lifted to --ks-kinpaku: detector focus ring + secondary action buttons,
design-system candidate caption + mini-ui "after" border, docs neon-case
secondary/hero-shot-hover, docs flow/chain arrows, homepage slop-teaser
hover + impeccable-card.
Dropped to --ks-rule: design-system comparison-stage frame, docs
neon-case-command frame, light-mode hero demo shell + split-label pill +
live-demo pin-note.
--ks-rule-strong is now unused outside its token definition.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The favicon was hardcoded to a muddy #d8a83a, duller than the header
logo's pristine kinpaku. Switch fill by OS theme via an embedded
prefers-color-scheme media query: near-black in light, pristine kinpaku
(#ffb900, matching --ks-kinpaku) in dark.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On wide viewports the hero's bright kintsugi seam sits under the
top-right nav cluster, where dark light-mode text and the toggle/GitHub
chips fight the gold. Add a contextual, light-only radial scrim anchored
to the corner: it feathers out by 70% (no hard edge), is faded via --hp
the moment the header glasses in on scroll, and is off below the mobile
drawer breakpoint. Dark mode needs no treatment (cream text reads fine
over the darker seam).
With the scrim carrying the corner, the toggle and GitHub counter become
refined hairline chips in light mode — neutral --ks-rule border, no fill
(the GitHub pill previously used the gold --ks-rule-strong and a solid
background).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The `i-` prefix install option was a holdover from the multi-skill era.
With a single `impeccable` skill it only ever renamed that one skill to
`i-impeccable`, while the install message wrongly advertised `/i-audit`
style commands that never existed, and the unscoped rename could clobber
unrelated third-party skills in the same harness folder.
- Drop `--prefix=`, the interactive prompt, and all prefix machinery
(renameSkillsWithPrefix, prefixSkillContent, detectPrefix, undoPrefix,
prefixedCommandHint, isImpeccableSkillName).
- Add migrateUnprefixImpeccable: install --force and update rename any old
`<prefix>impeccable` back to canonical `impeccable` before the fresh copy
lands, scoped by name so foreign `i-*` skills are left untouched.
- Fix FAQ + editorial that wrongly described pinned commands as `i-`
prefixed (pins are bare `skills/<command>/` dirs).
- Tests now exercise the real exported migration, not a reimplementation.
- CLI 2.3.1 -> 2.3.2 with a changelog entry.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The lockup read a touch heavy. Drop the brand wordmark to 400 across the
header, footer, and the .ks-wordmark kit primitive so it stays consistent
everywhere. Alumni Sans was only loaded at 500/600/700, so 400 is added to
the font request (otherwise it would snap back to 500). DESIGN.md synced.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Closes the documentation gap from #177: how to update an installed
version was nowhere on the site. Install and Update now sit as paired,
equally-visible commands, with `npx impeccable skills check` and the
Claude Code `/plugin` path called out alongside.
Also a full pass on the section's composition:
- Commit to left-aligned asymmetry so content has one spine and the gold
seam owns the right edge, instead of floating left-of-center
- Make the install command pop: bright kinpaku frame + gold `$` prompt +
left-aligned mono so it reads as a runnable line, not a decorative chip.
Update box mirrors it one notch quieter in patina (the "updated" state)
- Group install/update/alternatives tightly, rule off the secondary
surfaces, drop the duplicate "Get started:" closing label
- Repurpose the "Stay updated" cell to "Follow along" so it stops
colliding with the real Update command
FAQ already had a strong #update entry; added the `skills check`
companion for parity. Getting-started tutorial gains a short update note
after Step 1. Both themes synced.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(home): command-wheel contrast, slop copy, line-length
Address P1/P2/P3 findings from /impeccable critique of the homepage:
- Command wheel (The Language): off-center command names floored at
~1.43:1 contrast were illegible (WCAG 1.4.3 fail) and hid most of the
23-command vocabulary. Raise the fisheye opacity floor 0.25 -> 0.62,
MIN_SCALE 0.35 -> 0.52, and lift the base color from --ks-text-muted to
--ks-text. Off-center now measures >=4.59:1; full list stays scannable
while gold + size + weight still carry focus.
- Slop section copy: rewrite all 7 discipline cards off the uniform
"No X. No Y. No Z." triad into varied cadence with positives, and lead
the section with what Impeccable does instead of the "Skills can't..."
negation pivot. Drops the en-dash joiners too.
- Line length: cap .downloads-rebuild-note (was unbounded, ~102ch) and
tighten the homepage .section-lead 68ch -> 62ch (rendered ~86 actual
chars/line).
The diagonal plinth ramp on the slop grid is intentionally kept per
design preference.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(home): de-warm and brighten the text ramp
Warmth now lives only in the gold accents and surfaces, not the type.
Body, headings, and the secondary tiers read crisp on lacquer instead of
mushing into the warm floor:
- --ks-champagne 84% .035 82 -> 91% .006 90 (headings/strong, now neutral;
token name kept for compatibility)
- --ks-text 81% .03 82 -> 88% .008 90 (body)
- --ks-text-muted / -faint / -mute-deep lifted and de-warmed to match
DESIGN.md frontmatter + prose synced to the new values.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(home): outlined testimonials + cleaner hero boundary
Testimonials:
- Flatten the double container: drop the t-plinth shelf wrapper (markup +
CSS); the marquee sits directly in the section and carries its inset.
- Outlined cards: no fill, 1px solid neutral border (oklch .64 0 0 / .22),
no dead drop-shadow. Removes the mushy gray-on-gray blend and the stacked
dotted-divider + gold-hairline chrome.
- Section has no background of its own (rides the body lacquer gradient) and
no top padding, so cards sit right under the hero divider.
Hero:
- Drop the bottom fade and the top nav scrim; the kintsugi art runs at full
strength. A 1px neutral border-bottom (matching the card border) marks the
testimonials boundary instead of a wash.
- "How it works" is the kit ghost link (white), not an outlined button.
Foundation/slop cards: lift the surface 9% -> 15% so they read as raised
specimen cards instead of vanishing into the ground.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(live): quieter, more refined picker chrome
Gold is reserved for the brand mark and the active control instead of
ringing every container. Applied to the homepage demo, /live-mode, and the
real injected picker (skill/scripts/live-browser.js, rebuilt into the
harness dirs):
- Container: neutral 1px hairline + tight neutral shadow (was a 1.5px gold
border + gold halo ring); radius 10px -> 8px.
- Active toggle: crisp graphite pill with gold text (was a murky kinpaku-dim
wash).
- Internal control borders (action pill / input / count): neutral hairline
(was a warm gold rule); configure-row controls share one 30px baseline.
- Pick outline: crisp 1.5px line, no soft gold glow ring; tighter radius.
- Demo browser chrome: small uniform neutral dots, neutral URL pill, slimmer
bar; frame edge neutral hairline + tighter shadow that registers on dark.
DESIGN.md "Live Mode Picker" spec + "Picker Is Brand Rule" updated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(design): neutral default hairline (--ks-rule)
The default border/divider token was a warm gold hairline, used ~200x as
the site-wide default border — so every small label, pill, counter, card,
and divider carried warmth. Redefine it neutral so borders read clean;
gold stays where it signals.
- --ks-rule oklch(58% 0.065 82 / 0.32) -> oklch(78% 0 0 / 0.16)
- --ks-rule-strong (active/focus/brand borders) unchanged, still gold
- GitHub star pill: explicit near-white border (oklch 92% 0 0 / 0.18)
- DESIGN.md hairline mirror + prose synced
Verified across home, /design-system, and /docs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(home): testimonials separator + visible star-pill border
- Move the dotted accent to the bottom of the testimonials (neutral dots)
as a deliberate separator into the slop section; drop the oversized
bottom padding to 1em so cards sit near the separator.
- Star-counter pill: solid oklch(80% 0 0) border. The previous near-white
at 0.32 alpha rendered as faded mid-gray on the near-black pill; a solid
light border reads as the intended white hairline.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(design): neutralize body text (--ks-text)
Drop the last bit of warm chroma from the body text token; it still read
slightly warm at 0.008 chroma.
--ks-text oklch(88% 0.008 90) -> oklch(88% 0 0) (pure neutral)
DESIGN.md mirror + prose synced.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(design): neutral text everywhere
Zero the residual warm chroma across the rest of the text ramp so no text
tier carries warmth (warmth lives only in gold accents + surfaces).
- --ks-champagne 91% .006 90 -> 91% 0 0
- --ks-text-muted 72% .01 90 -> 72% 0 0
- --ks-text-faint 62% .008 90 -> 62% 0 0
- --ks-text-mute-deep 52% .008 90 -> 52% 0 0
(--ks-text was already neutralized.) DESIGN.md mirror + prose synced.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(home): crisp, neutral Desloppification section
- Foundation card background: oklch(15% .004 95) -> oklch(15% 0 0) (neutral
graphite) so cards read crisp, not warm.
- Plinth hatch: kinpaku gold -> neutral (oklch 80% 0 0 / .07) on a neutral
base; the gold hatch was washing the section champagne.
- Remove the plinth bottom mask-fade so the pedestals end on a clean edge.
Gold stays only on the card icons as the accent.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(home): drop the homepage warm-token overrides
The homepage redefined --ks-rule, --ks-rule-strong, and --ks-muted to warm
values locally (an old "busier surfaces" tweak), so homepage borders and
secondary text stayed champagne even after the global de-warm. That's why
the Desloppification cards still read warm.
- Remove the --ks-rule / --ks-rule-strong overrides; inherit the global
tokens (neutral default border, gold strong/active border).
- Alias --ks-muted to the global --ks-text-muted (no divergent value);
legacy code still reads the --ks-muted name.
Result: all homepage borders + secondary text are neutral; gold stays on
accents (icons, mark, CTAs, active/focus).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(design): canonical code tokens (inline + block)
Code styling was all over the place: homepage inline code was gold-on-gold,
the slop CLI was a one-off gold-on-raised-lacquer, downloads used a separate
--card-cmd-* set, docs used yet another. Add one shared token set and point
the canonical surfaces at it.
New :root tokens:
- --ks-code-fg / --ks-code-bg / --ks-code-radius (inline: neutral chip)
- --ks-code-block-fg / -bg / -border / -radius (block/CLI: lacquer terminal)
- --ks-code-cmd (code that's a command link)
Migrated: homepage inline code (was gold -> neutral chip), slop-teaser-cli
(the "weird color" -> neutral terminal), downloads-cmd, and the docs inline +
fenced-block rules (now the token source of truth; block text also neutralized).
Remaining pages (designing, changelog/faq, detector, case studies) swept next.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(design): sweep remaining pages onto code tokens
Point the rest of the site's code rules at the shared code tokens so inline
code and blocks are consistent everywhere:
- Inline code (designing, changelog, faq): gold -> neutral chip
(--ks-code-fg / --ks-code-bg).
- Detector rule pills + table cells: code text -> --ks-code-fg.
- Neon-mirai case-study code block -> --ks-code-block-* tokens.
Command tags (the gold /command pills: spread-flow-cmd, docs-flow-cmd,
designing-phase-cmd, why-ci-cmd, etc.) are intentionally left as their own
interactive category, not generic code.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(home): polish "The Language" section
- Command tags (/polish, /adapt): gold command text on a neutral code chip
(--ks-code-cmd / --ks-code-bg), dropping the muddy gold-tint border.
- Commands panel kept as the solid oklch(0.17 0 0) panel (no border).
- Demo preview cleaned up to a single framed split: strip the grid ::before,
the gold-grid/radial-glow container background, and the inner drop-shadow;
before-half inherits the panel, after-half is near-black, with one thin
neutral border on the demo itself (caption sits outside it).
- Periodic table: crisp flat neutral graphite tiles. Removed the JS-inlined
category bg (var(--cat-*-bg)) + 1.5px colored border + hover drop-shadow,
the gold-leaf ::before texture, the ::after accent line, the inset box-shadow,
and the gold hover glow. Now a 1px neutral border, white symbols, readable
neutral names, and a clean neutral-border hover with no shadow.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(design): roomier inline-code padding
Inline code chips were tight top/bottom (the homepage one was only 0.05em).
Add a --ks-code-pad token (0.3em 0.5em) and point every inline-code rule at
it so the chips have consistent breathing room.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(home): drop non-steering commands from the command palette
impeccable, init, extract, document, and live are setup/management commands,
not steering verbs. Filter them out of the palette (fisheye + mobile carousel)
via a shared PALETTE_EXCLUDED set. They stay in the periodic table, which is
rendered separately by framework-viz.js.
Palette: 23 -> 18 commands.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(designing): flatten the pre-ship nested box
The pre-ship cards were a box-in-box: a legacy .designing-polish-grid panel
(cream bg + L/R/B border + padding, from docs-visuals.css) wrapping cards that
already have their own border + fill. Override the grid to a plain transparent
layout so the three cards are the only surface.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(designing): remove step counter + flatten design-debt boxes
- Drop the cryptic "03 · 04" pre-ship step counter (.designing-polish-band-meta)
and tighten the band to a single bottom hairline.
- Design-debt: flatten the box-in-box (bento plinth > tile > stage). The
.designing-maintain-stage no longer adds its own border + fill; the demo
sits directly in the bento tile.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(designing): neutralize code/terminal/panel surfaces
The terminal block, surface-cmd chips, command pills, live-frame, and other
dark panels used a slightly-warm dark fill (oklch 1X% 0.006 95). Drop the warm
chroma so they read neutral like the rest of the de-warmed site; the page
ground + deep surfaces stay lacquer-warm.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(designing): neutralize inline code + live-mock picker chrome
- Inline code: the phase-sub and avoid-title code were still gold; point them
at --ks-code-fg so all inline code reads neutral (gold stays only on command
*links*).
- docs-viz-live mock: bring the duplicated picker chrome in line with the
refactored neutral treatment — neutral 1px container borders (no gold halo),
neutral active "Pick" pill (was the kinpaku-dim wash), crisp pick outline
(no glow), tighter radii.
- CTAs (SEND ME ONE, Accept): pale-cream kinpaku-pale -> solid kinpaku gold.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(designing): flatten lanes + avoid sections
- Brand/Product lane mock cards: drop the inner border+fill box; the mock sits
directly in the bento tile, separated by a top hairline (no plinth>tile>mock
nesting).
- "What to avoid" list: flatten the boxed list cards into a clean divided list
(hairline separators, no per-item border/fill).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(designing): clean up the Brand/Product lanes
- Drop the bento plinth (0.17 fill + 8px gutter that drew the weird gutter
"borders") and the tile fill; the two lanes sit on the page split by a single
center hairline.
- Brand mock title used the pinstripe display face at 1.6rem (reads broken at
that size, the "champagne text"); switch it to the clean body face so it
matches the product mock title.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(site): update GitHub star count to 31k
31,188 stars as of now; header pill + aria-label were stale at 30k.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Add Neo Kinpaku light mode across the site.
Wire theme persistence and a header toggle, then layer light-mode overrides for docs viz contrast, command demos, live-mode pathway cards, and the designing/home surfaces.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(designing): replace em dashes flagged by prose validator
Brand/Product lane copy used em dashes ("the deliverable —", "the task —");
swap for colons per STYLE.md so the Cloudflare build's validateProse passes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(designing): address Cursor bugbot nits
- Fold the duplicate .designing-avoid { gap: 0 } override into the original
rule (the gap: 18px was dead code).
- Drop the leftover el.style.boxShadow = 'none' in the periodic-tile deactivate
handler — activate no longer sets a box-shadow, so this only left a dead
inline none that could suppress a future CSS shadow.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
The .codex/agents sidecar + boot-time self-heal it described was reverted
in CLI v2.3.1 (nested in-skill agent is the whole delivery now), so the
bullet no longer matched shipped behavior. Removed from the changelog and
the skill-v3.5.0 GitHub release notes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codex auto-discovers subagents bundled inside an installed skill's own
agents/ folder, so the separate .codex/agents/*.toml sidecar was redundant.
- cli: remove installCodexAgents/isCodexLikely and their install/update calls
- context.mjs: remove the CODEX_AGENT_MISSING self-heal directive
- build: drop codex agentFormat so no top-level .codex/agents is emitted; the
nested in-skill .toml bundling is the whole delivery
- remove the tracked .codex/agents/*.toml and the gitignore exception
- docs + build.test.js updated for the nested layout
- CLI patch version bump; skill version unchanged
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(live): correct the generation shader's capture + halftone on dark and textured surfaces
The live-mode "ink-wash" loading shader rendered correctly on light
elements but broke on dark and textured ones. Root causes and fixes:
- Ground the halftone on the element's own background tone (new u_paper
uniform) instead of a fixed cream paper, so dark elements stop flashing
bright as the roller passes.
- Drive dot size by each cell's contrast from that ground, not absolute
darkness, so content (text, buttons) becomes the dots on light and dark
alike instead of inverting on dark elements.
- Cap the dot radius so a solid dark region stays separated dots rather
than flooding into a gold bar.
- Parse computed colors by rasterizing through a canvas, so oklch()/color()
tokens resolve instead of falling back to white.
- Two-stage dissolve (flatten to ground, then dots emerge) so the raw
element never bleeds through the band's soft core/trail.
- Carry the capture's alpha through the shader so rounded corners and
transparent regions show the live backdrop instead of rendering black.
- When an element is transparent up to the root but its backdrop comes from
an ancestor's image or a covering layer (e.g. a hero art div), capture
that ancestor and crop to the element. Fixes the homepage hero heading
capturing on white, and embeds the real backdrop in the model upload too.
The halftone ground is sampled from just outside the element so it tracks
the true backdrop rather than a muddy average of the content.
Adds /shader-lab, a standalone harness that runs the real capture + shader
pipeline against a matrix of background shapes (light, dark, gradient,
image, glass, rounded, and a homepage-hero replica) with raw vs
capture+shader side by side. The capture/shader code is copied from
live-browser.js and kept in sync.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(live): clear the cached color-parse canvas before each fill
Cursor Bugbot (PR #171): cssColorToRgb01 reuses a cached 2D context, so a
semi-transparent input (alpha 0<a<1, which isTransparentColor lets through)
blended source-over with the previous call's pixel, making the result depend
on call history. clearRect before the fill makes each call independent.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
#158 added the live-mode manual-edit subagent but did not commit the
.agents harness copy. Regenerated by bun run build; commit keeps the
tracked harness dirs in sync so the release script's clean-tree check passes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pick an element, Edit copy in the browser, and on Apply a subagent
rewrites the real source the text renders from and repairs anything wired
to it. Folds into the v3.5.0 Live Mode bullet alongside the Steer bar.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the retired light/magenta OG card with a brand-true Kinpaku
card (lacquer ground, champagne Alumni Sans headline, kinpaku-gold
accent, kintsugi-seam art). Headline: "Design fluency for every AI
harness." Command count is read live from command-metadata.json.
- scripts/generate-og-image.js: rewritten to render the Kinpaku card
via Playwright at 2x and downscale with sharp; outputs og-image-v2.jpg
- Base.astro: emit og:image + summary_large_image on every page with a
sitewide default (was homepage-only); pages override via ogImage prop
- og-image.jpg renamed to og-image-v2.jpg for cache-busting; index.astro
reference updated
- CLAUDE.md: document `bun run og-image` regeneration + cache-bust steps
- .gitignore: ignore .og-build scratch
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Syncs the 13 committed harness SKILL.md files with skill/SKILL.src.md.
The "Verify contrast." Color bullet was added to source in 9ffd3211 but
that commit skipped the harness rebuild, leaving the outputs stale. This
is plain `bun run build` output; no source change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Benchmarked impeccable@2.1.9 (last jsdom-based release) against the current
static engine on an identical 160-file HTML corpus, same Node runtime, 3 runs:
6.8s -> 0.34s median, ~20x faster (~43ms/file -> ~2ms/file). Replaces the
single-engine throughput figure with the before/after.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 'Detector: 7 new rules' line undercounted (14 rules landed since the
pre-rewrite baseline; one of the listed 7, italic-serif, actually shipped in
v3.0.7). It also omitted the bigger win: the jsdom-free static engine (#156).
Correct the count across the skill, CLI, and extension entries, and add the
engine rewrite with real numbers (~4ms/HTML file, 71-file corpus under 200ms,
measured via bun run bench:detector).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The /docs/impeccable editorial described bare /impeccable only as freeform
design / fallback. It now also covers the no-command behavior: it reads setup
state, the dirty tree, the last critique, and a quick detector pass, then
recommends the highest-value next commands. /designing left as-is (it already
guides command choice per phase).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The full-bleed breakout baked the (100vw - 1500px)/2 gutter into each edge
tile's padding for title alignment. On the leftmost span-4 tile (DESIGN.md)
that gutter grew faster than the column, so past 1500px the inner .why-dm-grid
got squeezed as the viewport widened.
Move the cap to .why-bento itself via margin-inline that only cancels
.site-content's clamp side padding: below 1500px the rail stays edge-to-edge,
at/above 1500px it caps at 1500px and centers with the page background on the
sides. Tile content still aligns with the section headings, and columns stop
growing so the mockups hold their size.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bare /impeccable changelog defaults to Skill-only; CLI, Extension, and All
toggle the rest. Component is derived from each entry's id prefix (cli-/ext-),
so no per-entry tagging. Accessible button group, kinpaku segmented styling,
shows all with JS off.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
.cf-items code and .cf-faq-answer code never set a font-size, so inline code
rendered at 1em and looked oversized next to the body text (the page doesn't
load main.css's global code rule). Match the 0.92em already used by
.cf-faq-question code.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rework context-signals' detect target after review: a URL meant a costly
Puppeteer render (and a probed port might not even be this project), and the
index.html-or-bail fallback failed most real apps (no root index.html).
New priority: (1) the scannable markup/style files in the dirty git tree
(what the user is working on, small and local); (2) a local source dir
(src / app / components / pages / public — the detector walks these and skips
node_modules / dist / build); (3) a root index.html, else the project root as
a last resort when there's code. Emits `scan.targets` (a list) + `scan.via`.
Never a URL.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Since the jsdom removal the static HTML/CSS analysis is fast (~4ms/file) and
covers every rule, so the regex-only `--fast` path only loses coverage (it
ran ~10 of 41 rules) for no real speed win. It's a foot-gun: a `--fast` scan
can read "clean" because most rules silently don't run.
Deprecate gracefully rather than hard-remove: the flag is still accepted (so
existing CI scripts don't break) but ignored, with a one-line stderr notice,
and the full scan always runs. Dropped from --help and the example. Removed
the `--fast` suggestion from the many-files warning and from critique.md's
scan guidance.
Ships to users via a CLI release (npm) and rides the next skill release in
the bundled detector. Tests updated to assert the deprecation behavior.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reshape of the "/impeccable suggest" proposal in #159. Instead of adding a
24th command (menu pollution + the command-add tax + its own discoverability
problem), upgrade the path users already hit: bare `/impeccable` with no
argument.
- New skill/scripts/context-signals.mjs gathers cheap, deterministic signals
(setup gaps, register, latest cached critique score, git change scope, a
dev-server port probe, and a `scan.detectTarget` for the detector) and emits
JSON. It does NOT score or rank, and it does NOT run the detector itself
(the engine isn't importable in an installed skill, and shelling npx+jsdom
would risk a hang) — the agent reasons over the raw signals.
- SKILL.md routing rule 1 now leads with the 2-3 highest-value next commands,
each with a reason from the signals, then the full menu. Never auto-runs;
always confirms. Reuses init's "Recommend starting points" vocabulary. When
a project has never been critiqued it offers critique; when scan.detectTarget
is set it runs `npx impeccable detect --fast --json` and folds the hits in.
- Export extractRegister from context.mjs for reuse.
Stays 23 commands; no metadata/pin/site-data changes. Unit-tested, including a
regression guard for porcelain leading-space path parsing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The hero clipped horizontally on phones: the collapsed grid used a plain
`1fr` track whose min-content floor wouldn't shrink below the demo's 460px
browser frame. Switch the mobile track to minmax(0,1fr) so it shrinks to the
viewport and the frame clips its own content. Drop the container's redundant
56px side padding on mobile so the hero uses the standard 24px gutter.
Also: collapse the demo's hotel-mock nav to logo + Book on mobile (its full
4-link nav overran the narrow frame and clipped mid-word), and give the
scan-terminal `overflow-x: auto` so long lines scroll instead of clipping.
The title keeps its design-system clamp (no mobile shrink): Alumni Sans
Pinstripe is condensed, so it fits at 54px down to 320px, holding a ~3.2×
hero hierarchy over the body.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reshape of the "/impeccable suggest" proposal in #159. Instead of adding a
24th command (menu pollution + the command-add tax + its own discoverability
problem), upgrade the path users already hit: bare `/impeccable` with no
argument.
- New skill/scripts/context-signals.mjs gathers cheap, deterministic signals
(setup gaps, register, latest cached critique score, git change scope, a
dev-server port probe) and emits JSON. It does NOT score or rank — no
brittle weights table — the agent reasons over the raw signals.
- SKILL.md routing rule 1 now leads with the 2-3 highest-value next commands,
each with a reason from the signals, then the full menu. Never auto-runs;
always confirms. Reuses init's "Recommend starting points" vocabulary.
- Export extractRegister from context.mjs for reuse.
Stays 23 commands; no metadata/pin/site-data changes. Unit-tested, including
a regression guard for porcelain leading-space path parsing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codex reads custom subagents from .codex/agents/*.toml, a directory
separate from where it reads skills (.agents/skills). Skill installers
(notably `npx skills add`, see vercel-labs/skills#1290) only carry the
skills/ subtree, so the asset-producer agent was never delivered.
- build: bundle the codex .toml inside the skill dir for the variants
Codex loads as a skill (agents, codex), so it travels with the skill.
- cli: skills install/update now write .codex/agents/ for Codex-likely
projects (a .agents target or a global ~/.codex); update heals a
missing sidecar. Non-Codex projects are untouched.
- context.mjs: on boot under a Codex install, emit a self-healing
CODEX_AGENT_MISSING directive pointing at the bundled copy when the
project's .codex/agents/ definition is absent. Self-resolves on copy.
CLI 2.2.0 -> 2.3.0 (published). Skill stays 3.5.0 (unpublished); the
note is folded into the existing 3.5.0 changelog entry.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two more testimonials on the homepage marquee: faizan10114's "I will fight
anyone..." (a second card from him, placed in the other row) and eclecticV's
"This is the best plugin ever created imo." (first sentence only). New
avatar for eclecticV; faizan reuses his existing one.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Swap the extension icons (16/32/48/128 + source SVG) to the new gold
kinpaku brand mark on a dark rounded square, replacing the old diagonal
stroke. Update STORE_LISTING.md: the detection count is now 41 (was 24),
and the WHAT IT DETECTS lists are refreshed to the current ruleset
(26 AI-slop + 15 quality rules).
Still v1.1.0 (not yet submitted to the Chrome Web Store).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Add neo kinpaku design system page
* skill: rip out baked-in category recipes and saturated-default motion tropes
Programmatic bias mining (impeccable-evals) traced four major defects
back to specific lines in this skill that contradicted SKILL.md's own
first-order-reflex warning:
- brand.md "Pairing and voice" prescribed four category→aesthetic
recipes (editorial → serif+sans, tech/dev/fintech → tight tracking,
consumer/food/travel → script/display serif, creative → rule-break).
These directly drove OpenAI's 76% extreme-negative letter-spacing
on tech briefs and Anthropic/Google's 28-34% italic-serif-display
slop on editorial/food briefs. Replaced with one sentence: the
shape depends on the brand, not on the brand's category.
- brand.md "Brand permissions" had "Typographic risk. Enormous
display type, unexpected italic cuts, mixed cases, hand-drawn
headlines, a single oversize word as a hero." — a four-for-one
slop driver behind 97% OpenAI comically-large H1, 42% bad-SVG
illustration, and the editorial-italic slop. Deleted outright.
- typeset.md and teach.md repeated the same category recipes;
trimmed to the principle without the recipe.
- SKILL.md Typography: added a hard hero-H1 ceiling (clamp() max
≤ 6rem ≈ 96px), with a <codex> block to make it explicit since
OpenAI over-indexes here (97% ≥128px vs 24% for Anthropic).
- animate.md, bolder.md, brand.md: removed "staggered reveals" and
"scroll-triggered transitions" as the prescribed default ambitious
motion. By 2026 that's the saturated AI tell, not a choreography.
Reserved stagger for legitimate list-sibling rhythm.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* skill: anti-cream + codex-specific defect bans + universal slop bans
Second pass after measuring more biases against the eval corpus.
- SKILL.md Color: explicit "cream/sand/beige body bg is the saturated
AI default of 2026" rule. Tone down the "tint every neutral" line so
it doesn't read as "default to warm-tinted near-white" (which OpenAI
hits at 74% and Anthropic at 31%-47%).
- SKILL.md Absolute bans: add universal bans for two slop patterns
detected at 55-95% across providers — tiny uppercase tracked eyebrow
above every section (the 2023-era kicker that's now AI grammar) and
numbered section markers (01/02/03). Also explicit "text that
overflows its container is the universal defect on tablet/mobile."
- SKILL.md Absolute bans → <codex> block: ban the GPT-specific defects
Paul annotated repeatedly — `border:1px solid` + soft-wide-shadow
(≥16px blur) "ghost cards", `border-radius:32px+` over-rounding,
hand-drawn/sketchy SVG illustrations (loose-sketch / *-sketch classes,
feTurbulence paper-grain filters), repeating-linear-gradient stripes,
"X theater" AI-slop copy phrases.
- SKILL.md Motion → <gemini> block: the image :hover transform tell
(38% Google skill-on rate). Hover effects on images add no info; the
image isn't an action target. Animate card chrome, not the image.
- SKILL.md Typography: hard display letter-spacing floor ≥-0.04em
(OpenAI defaults to -0.075em → cramped). Existing hero ceiling
<codex> block extended with the letter-spacing rule.
- codex.md Step A example: stop seeding "warm-grounded (deep oxblood +
cream)" as the warm-palette template, which primes the cream default.
- colorize.md Tinted backgrounds: stop printing the literal cream
recipe `oklch(97% 0.01 60)`; replace with brand-anchored guidance.
- document.md examples: warm-ash-cream → cool-paper so the example
doesn't seed cream as the canonical neutral example.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* skill: universal anti-slop bans + contrast/font-count/all-caps-body rules
Third pass after measuring the rest of the cross-provider matrix:
- Color: explicit "Verify contrast" rule. Low-contrast text fires at
68% across all providers skill-on (90+% off). The most common
failure is muted gray body on a tinted near-white; light-gray-for-
elegance is named as the single biggest cause of unreadable AI
pages.
- Typography: max-3-font-families rule. Overused-fonts (>4 families)
fires at 28% Anthropic / 36% Google / 0% OpenAI skill-on; >50% off.
Also: universal "no all-caps body copy" (moved from brand-only ban
to Shared design laws since product-register also overuses caps).
- Copy: anti-aphoristic-cadence ban targets Anthropic's signature
"X. No Y." / "X. Just Y." voice (63% skill-on copy-slop rate, 77%
off — the worst rate in the matrix). Once-is-voice / three-or-more-
is-tell framing per the runner's copy-slop detector.
- Copy: anti-SaaS-buzzword-string ban with the literal phrase list
the detector watches for (streamline/empower/supercharge, trusted-
by-leading, best-in-class/enterprise-grade/cutting-edge, etc).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* skill: strengthen anti-cream rule across full warm-neutral band
Smoke validation showed the cream fix worked for Google + OpenAI but
Anthropic Sonnet italian-restaurant still shipped `--paper: oklch(90%
.018 88)` — cream just outside the L≥95% band the rule cited.
Broaden the rule:
- Band: OKLCH L 0.84-0.97, C < 0.06, hue 40-100 (was 95-97% / 60-95).
- Name the token-name tells explicitly (paper / cream / sand / bone /
flour / linen / parchment / wheat / biscuit / ivory) — the model
defaults to one of these regardless of what hex it lands on.
- Call out the specific brief patterns ("warm, traditional, family-
coastal-Italian" / "editorial-restraint") that the model translates
into cream by reflex. Then provide three explicit non-cream options:
saturated brand color, true off-white at C=0, or darker mid-tone.
Warmth in the brand is carried by accent + typography + imagery, not
by body bg.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* v3.2.0: skill bias-fix release
Bumps version from 3.1.1 to mark the four-commit skill cleanup that
rips out baked-in category recipes (brand.md), saturated-default motion
tropes (staggered reveals everywhere), the cream/sand body-bg AI tell,
codex-specific defects (1px+wide-shadow, over-rounding, hand-drawn SVGs,
stripes, X-theater copy), the extreme-letter-spacing default, and
universal slop bans (all-caps eyebrow on every section, numbered-section
markers, all-caps body, font-family-count > 3, aphoristic copy cadence,
SaaS buzzword strings). Plus a hard hero-H1 ceiling (clamp() ≤6rem) and
a Gemini-specific image:hover transform block.
Validated against ~190 post-fix samples — see impeccable-evals
biases tab for per-provider deltas.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* drop "no pure black/white" rule entirely
The rule was contested in the design world and causing more damage than
good — pushing every page into the tinted-near-white default which is
the cream/sand AI tell we already explicitly ban elsewhere. Vercel,
SVKMS, Brutalist sites, et al. use pure black/white successfully; the
skill shouldn't second-guess that.
Skill markdown deletions:
- SKILL.md Color: drop the "Never use #000 or #fff" bullet.
- color-and-contrast.md: drop the "Never Use Pure Gray or Pure Black"
subsection, the "Never pure black" table-row prescription, and the
"Avoid: Using pure black for large areas" bullet.
- colorize.md: drop the "NEVER use pure black or pure white for large
areas" bullet.
- polish.md: drop the "Tinted neutrals: No pure gray or pure black"
half of the bullet (the gray-on-color bullet survives).
Detector code (cli/engine):
- registry/antipatterns.mjs: remove the `pure-black-white` entry.
- rules/checks.mjs: remove the three `findings.push({ id:
'pure-black-white', ... })` emit points (inline #000 bg, Tailwind
bg-black class, plain-HTML scan path).
- engines/regex/detect-text.mjs: remove the two pure-black-white regex
rules (CSS `background: #000…` + Tailwind `bg-black`).
- detect-antipatterns-browser.js: regenerated via
scripts/build-browser-detector.js.
Tests:
- detect-antipatterns-fixtures.test.mjs: invert the assertion that
pure-black-white fires; expect it to NOT fire post-v3.2. Drop the
Tailwind bg-black-opacity edge-case test (no longer relevant).
- detect-antipatterns.test.js: drop the standalone "detects pure-
black-white in styled-components" test and remove pure-black-white
from the multi-detector assertions in PricingCard, globals.css, and
GlobalStyle.tsx tests.
166 bun tests pass; 24 node fixture tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* skill: strip example patterns from copy rules, strengthen gemini block
v3.2 rerun validation surfaced two issues:
1. Copy-slop detector fires more on Gemini under v3.2 (48% → 84%) than
under no-skill baseline. Root cause: the anti-aphoristic-cadence rule
printed the literal "X. No Y." / "X. Just Y." patterns as examples,
and Gemini imitated them as the recommended voice. Same recipe-becomes-
bias trap we hit with brand.md:116's "Enormous display type, unexpected
italic cuts, mixed cases, hand-drawn headlines" enumeration. Fix:
describe the cadence as a rhythm ("serious statement, then punchy
short negation") without printing literal patterns. Buzzword list
trimmed to a single inline phrase family rather than quoted strings.
2. Gemini image:hover transform Gemini-tell hadn't dropped (31% off →
32% v3.2). Strengthen the <gemini> block: explicit "Never animate
<img> elements on hover", call out the Tailwind group-hover:scale /
group-hover:rotate / group-hover:translate parent-hover patterns by
name (Gemini was reaching for these via Tailwind even though the
prior text talked about :hover on the image directly).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* skill: simplify context loading and inline register directive
Replaces load-context.mjs's JSON output with a tight markdown block from
the renamed context.mjs. The script now extracts PRODUCT.md's `## Register`
field and appends a `NEXT STEP:` directive naming the matching reference
(brand.md / product.md), which moved Gemini from skipping the register
load entirely to honoring it. Drops the `.impeccable.md` auto-migration;
makes IMPECCABLE_CONTEXT_DIR a lazy escape hatch consulted only when the
default paths come up empty.
Setup is now four bullets in one list. The DESIGN.md nudge is gone; in
its place, a "familiarize with the existing design system" step that
calls out CSS / tokens / running app as authoritative sources alongside
DESIGN.md. The standalone `### Register` H3 stays for the cascade rules
(task cue → surface → register field).
New LLM-backed test suite at tests/skill-behavior/ runs five scenarios
against claude-haiku-4-5, gpt-5.4-mini, and gemini-3.1-flash-lite via
Vercel AI SDK. Captures real tool traces, asserts on context.mjs calls,
brand.md loads, and teach.md fallback. Skips cleanly when API keys are
unset. 13-14/15 pass; only stable failure is the v3.2.0-era gpt-mini S4
"don't re-run" regression. Adds @ai-sdk/google as devDep and the
test:skill-behavior npm script.
Touches em-dashes in skill/SKILL.md and four reference files so
`bun run build:skills` passes its skill-prose validator. teach.md and
document.md drop their "re-run the loader to refresh session cache"
steps since the agent's own write is now the freshest source.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* skill: merge orphan reference files into command sub-skills + inline S-tier invariants
Two related restructurings:
1. SKILL.md now carries the cross-domain invariants that catch defects in any
project (contrast/placeholder/gray-on-color, similar-font pairing, text-wrap,
tabular-nums, centered-stack default, Flex/Grid choice, auto-fit grids,
semantic z-index, reduced motion, stagger vs section-fade, premium motion
materials, focus-visible, placeholders-aren't-labels, dropdown overflow trap,
button/link copy). Greenfield-only rules (theme picking, color strategy,
tinted neutrals) live under "New projects only".
2. Reference files merged into their command counterparts:
- spatial-design.md -> layout.md
- motion-design.md -> animate.md
- color-and-contrast.md -> colorize.md
- responsive-design.md -> adapt.md
- ux-writing.md -> clarify.md
- typography.md -> typeset.md (bolder.md redirected)
- cognitive-load.md + heuristics-scoring.md + personas.md -> critique.md
craft.md and shape.md "load references" lists updated to new file homes.
interaction-design.md stays standalone (no 1:1 command verb).
Net: 36 -> 27 reference files. Same content, fewer files, no orphaned
reference loaded only from craft.md.
Also extends the routing rules: if the user's first word doesn't match a
command but the intent clearly maps to one, load that command's reference
and proceed as if invoked.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* skill: add sub-command + existing-project scenarios; move sub-command load to step 2
Adds three new LLM-backed scenarios to tests/skill-behavior:
- S6: `/impeccable polish` → loads polish.md
- S7: `/impeccable audit` → loads audit.md
- S8: existing SvelteKit project (PRODUCT.md + DESIGN.md + src/app.css +
src/lib/components/*.svelte + src/routes/+page.svelte) → agent reads
at least one project code file to understand the existing design system
S6/S7 surface a real model-floor: gpt-5.4-mini reads brand.md, reads the
target index.html, and just does the polish/audit without ever loading
the sub-command reference. Stronger SKILL.md wording didn't move it.
Captured in the README baseline as a known weakness. Claude and Gemini
honor the load reliably.
To fix Gemini on S6/S7, sub-command reference loading is now Setup step 2
(right after context.mjs), not step 4 — placing it before the model gets
focused on "doing the work". Step 3 (design-system familiarization) is
tightened to require at least one project code read even when a
sub-command reference loads in step 2, so Claude doesn't laser-focus on
the sub-command flow and skip the broader exploration.
Two new fixtures: MINIMAL_LANDING_HTML (a tiny static landing page for
S6/S7) and SVELTE_PROJECT_FILES (a minimal SvelteKit scaffold with
tokens, components, and a routes/+page.svelte for S8). Both designed to
look real enough that agents treat them as production code.
Suite is now 24 tests across three providers; baseline is 21-22/24, with
the stable failures being gpt-5.4-mini scenarios 6 and 7.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* skill: add reveal-animation safety rule (must enhance, not gate visibility)
Class-triggered visibility transitions pause on hidden tabs and headless
renderers. The italian-restaurant smoke produced a build where 2 sections
shipped opacity:0 because the CSS transition never advanced past
currentTime=0 (timeline paused). Added one-liner under Motion to prevent
the antipattern: reveals must enhance an already-visible default, never
gate content visibility on a class-triggered transition.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* skill: restore prescriptive cream/sand/beige paragraph
Bisection across 5 historical skill commits on Gemini 3.5 flash fast
lane n=3 found that 0cf2debd was the peak quality state. The regression
between 0cf2debd and HEAD came from simplifying the long anti-cream
paragraph into a one-liner.
Restoring the paragraph (with em-dashes replaced by parens to satisfy
prose lint) recovers ~0.22pt average on Gemini vs HEAD, with the
largest gains on:
- 09-luxury-hotel: +0.50 (restores editorial drama in photo-led briefs)
- 10-food-magazine: +0.67
- 03-italian-restaurant: +0.51
The paragraph's load-bearing parts are the (a)(b)(c) alternatives that
give the model actionable replacements for cream-tinted body bg
("saturated brand color as body", "true off-white at chroma 0",
"darker mid-tone tinted neutral"). Without them, the one-line warning
left the model with no concrete alternative.
Cross-provider validation showed the pattern matches historical
behavior: Gemini benefits from prescriptive scaffold (+0.12 over off),
Sonnet is roughly neutral (+0.01), GPT-5.5 slightly regresses (-0.11
matching the v3.1.0 pattern of -0.11). The skill has never been
uniformly better than skill-off across providers; this is the closest
achievable state without provider-specific rework.
The structural improvements from the prior restructure stay (file
merges, S-tier inlines, routing rule extension, reveal-animation
safety rule).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: teach CLAUDE.md / AGENTS.md / DEVELOP.md about the skill-behavior tests
Adds the `bun run test:skill-behavior` script to the test commands lists
in all three docs. CLAUDE.md gets a full `### Skill-behavior tests`
subsection paralleling the existing Live-mode E2E one: how the suite
works (inlines source SKILL.md, scoped tools, asserts on the trace),
which providers it always runs (claude-haiku-4-5, gpt-5.4-mini,
gemini-3.1-flash-lite — all three every run), the eight scenarios, the
baseline (21-22/24 with stable gpt-mini sub-command-routing failures),
auth via repo-root `.env`, and how to add a scenario.
AGENTS.md gets the one-liner plus a paragraph in Testing Guidelines that
points contributors at the suite for Setup-touching edits (SKILL.md
Setup section, context.mjs, teach.md, document.md, register / sub-command
refs).
DEVELOP.md gets a short Testing section that didn't exist before, plus a
nudge in the "Test across providers" bullet pointing at the new suite as
the automated way to do that.
No code changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* detector: add 5 new antipatterns (em-dash-overuse, broken-image, marketing-buzzword, numbered-section-markers, aphoristic-cadence)
Consolidates eval-side detection logic into the canonical impeccable
detector. Before this change, the eval harness had its own duplicate
implementations of em-dash, copy-slop, and broken-image checks. They
now live alongside the existing 28 antipatterns in the impeccable
registry, available to the CLI, browser extension, critique skill,
and eval (via the existing slop grader child-process call).
New antipatterns:
- em-dash-overuse: 5+ em-dashes in body text content (threshold
permits legitimate prose use of em-dash; only triggers on AI
cadence-level density)
- broken-image: <img> with empty src, missing src, or src="#"
- marketing-buzzword: SaaS phrase list (streamline / empower /
supercharge / enterprise-grade / cutting-edge / etc)
- numbered-section-markers: repeated 01 / 02 / 03 sequence as
section labels — the AI editorial scaffold one tier deeper than
tracked eyebrow chips
- aphoristic-cadence: 3+ manufactured-contrast ("Not a X. A Y.")
or short-rebuttal ("Sentence. No clause." / "Sentence. Just
clause.") constructions in body text
Engine wiring:
- broken-image runs as a static-html element rule (selector: img)
and a fallback regex matcher (for non-HTML files)
- em-dash / buzzword / numbered / aphoristic run as regex
page-analyzers, factored into a new runTextContentAnalyzers()
helper that both detectText (non-HTML) and detectHtml (HTML)
call, so .html files get the same coverage as .css/.tsx
Tests: 166 detector + 12 browser + 24 fixture all pass.
Browser detector rebuilt (162.7 KB).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* skill: drop unvalidated anti-centering rule; add image-led hero carve-out
The anti-centering rule ("Don't default to centering everything") was
added without empirical support. We have a detector for it
(everything-centered, threshold ≥70%) that fires on 0 / 998 samples
in the corpus — never validated, never useful.
Meanwhile the rule was almost certainly responsible for collapsing
Gemini 3.5 flash's luxury-hotel skill-on output from the canonical
"full-bleed photo + centered overlay headline" cinematic hero (the
shape skill-off Gemini chooses 67% of the time) to a 50/50
magazine grid (full-bleed rate drops to 18% under skill-on, -49pp).
Changes:
- skill/SKILL.md #### Layout: drop "Don't default to centering..."
- skill/reference/brand.md ## Layout: drop the same rule; replace
with a positive carve-out — image-led briefs (hotels, restaurants,
magazines, photography) often want full-bleed hero with overlaid
menu and centered headline; let the photograph be the design
- skill/reference/layout.md: drop the assessment question and the
"asymmetric breaks centered-content pattern" framing
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Apply neo-kinpaku design system and improve live picker UX
Restyle the live picker to match the site kinpaku kit, persist pick mode
in localStorage, fix DESIGN.md color swatches in the parser, and land the
neo-kinpaku site refresh with new tokens, assets, palette script, and
detector rules.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add live Steer end-to-end: poll protocol, browser UI, and E2E harness.
Wire page-level Steer through the live server and agent poll loop with steer_done
unlock semantics, extend live.md for agents, and add smoke tests with LLM
handleSteer plus recovery for hidden heroes, HMR lag, and dev-tool overlays.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add experimental live-poll --stream mode; keep one-shot default for Cursor.
Stream keeps one process alive with ack-aware resume, but live.md documents
that Cursor should stay on one-shot background notify after testing showed
~5s pickup vs sub-second on exit-based notify.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Sync harness output and fix build validators for poll stream release.
Regenerate provider skills after live-poll --stream work, update homepage
detection counts to 41, and replace em dashes in site/skill copy so
bun run build passes prose and count checks.
Co-authored-by: Cursor <cursoragent@cursor.com>
* homepage: add testimonials marquee section
A two-row testimonial marquee on a tinted graphite plinth, sitting
between the hero and the slop teaser.
29 testimonials sourced via api.fxtwitter.com (lightly cleaned: leading
@-mention reply targets stripped, trailing self-links removed). Avatars
downloaded into site/public/assets/testimonials/ so they're served
locally. Quote order curated for impact — both rows lead with the
punchiest quotes (Ben Davis spotlight, "Impeccable > Claude design",
"THIS. This shit works.", "Uninstall whatever frontend skill you're
using.") so the first viewport is loaded with the most memorable
testimonials.
Engineering notes:
- Section uses width:100vw + margin-left:calc(50% - 50vw) to escape
main.site-content's max-width + side padding (cards now clip cleanly
at the actual viewport edges).
- Marquee runs at 110s linear infinite. Both rows share the same
duration so on-screen speeds match; track is doubled so the loop
back to 0 reads as continuous.
- Hero min-height reduced from 100svh to calc(100svh - 115px) so the
dotted divider and top of row A peek above the fold on landing,
signalling the section is there.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* homepage: keep the hero demo clear of the fixed header on short viewports
The hero centers its content in the full viewport (the site header is a fixed
overlay), so on shorter screens the tall Live Mode demo tucked under the nav.
Raise the hero's top padding above the 97px header (113px wide, 108/92px when
stacked) so content always pins below the header while still centering on tall
viewports, and cap the demo frame to the viewport so the whole demo stays on
screen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add steer voice input and refine processing animation.
Wire Web Speech API on the Steer mic with auto-submit, block Cursor's preview browser with a clear message, and replace truncated "Working" text with a dots-only processing state.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add agent poll connectivity indicator and tighten global bar spacing.
Surface poller state on the Impeccable mark via SSE and /status, with an instant disconnected tooltip, steer timeout failsafe, and matched brand/chat section gaps.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix steer focus to allow page text selection without losing type-to-steer.
Blur the hidden steer input on page interaction, pause refocus during selection gestures, and reschedule focus recovery after clicks and cleared selections.
Co-authored-by: Cursor <cursoragent@cursor.com>
* site: rework "Design in production" section glyphs and audience band
Put the three how-it-works steps back into thin-line cards and drop the
overused browser-chrome bars from each glyph. Redraw the step 2 and 3
visuals to mirror the real Live Mode UI: step 2 shows the on-canvas pick
outline with an attached comment bubble, step 3 shows the floating
contextual accept bar plus the source-write confirmation. Re-treat the
audience tiles as verdigris-lined text (no card box) under a "Who it's
for" eyebrow, so each role reads as distinct from the gold step band.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add live insert mode with HMR-safe placeholder recovery.
Ships insert picking, scaffold helpers, variant cycling fixes for hidden
variants, and placeholder snapshot/recreation so Astro HMR does not drop
the wait-state box or re-anchor to the hero container.
Co-authored-by: Cursor <cursoragent@cursor.com>
* site: mobile pass — hamburger nav + designing hero overflow fix
The header was rendering inline nav links + GitHub button that overflowed
narrow viewports (~363px). Pre-existing display:none hacks hid Designing
and Live to make the row fit, but those items still belonged in the menu.
Header.astro: added a hamburger toggle button + inline script. The right
cluster (nav + GitHub) becomes a collapsible drawer below the header on
mobile, with data-nav-open driving the open/closed state and animating
the two-line glyph into an X.
kinpaku-kit.css: hamburger button (kinpaku-bordered glyph), mobile drawer
panel (solid lacquer-deep bg, hairline separators between rows, full-width
tappable rows), and overrides for the older sub-pages.css mobile rules
(horizontal-scroll mask on the nav, hidden [data-nav="home"] item, hidden
GitHub star label) — all redundant now that the drawer surfaces everything.
home-kinpaku.css: dropped the @media (max-width: 560px) block that hid
Designing / Live / GitHub. The drawer pattern shows them all.
designing-kinpaku.css: hero h1 "Designing with Impeccable" was overflowing
at narrow viewports. Three fixes:
- grid-template-columns 1fr → minmax(0, 1fr) so the column shrinks to
fit container instead of growing to "Impeccable"'s 472px intrinsic
min-content width.
- mobile h1 size override (clamp(2.2rem, 11vw, 3rem) at <=480px) since
the display token's 3.4rem minimum is sized for desktop hero impact.
- hide the decorative loop-wheel SVG below 600px (was overflowing 22px
past the right edge).
Verified clean at both 363px and 403px viewports across /, /docs,
/docs/animate, /slop, /designing, /live-mode. scrollWidth matches viewport
width on every page (no horizontal scroll).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* detector: refine new rules + run provider tells in browser env
Follow-up to the detector port (rules landed in 7648af00):
- oversized-h1: flag long headlines set at display size, not punchy
one/two-word heroes (length, not size alone, is the tell)
- provider tells (--gpt/--gemini) now always run in a real browser env
(detector page, live overlay, extension); gating is a CLI-output
concern only, applied in the Node engine return paths
- move theater-slop-phrase into checkHtmlPatterns so it runs in the
bundled browser path, not just CLI/static (browser bundle excludes
detect-text.mjs)
- hero-eyebrow-chip overlay highlights the eyebrow, not the heading
- gemini-tells fixture: data-URI images so the hover-zoom renders
- rebuild browser bundle
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* site: migrate /detector lab to neo-kinpaku design system
Rebuild the detector lab tool shell on --ks-* tokens (lacquer ground,
gold hairlines, champagne/mono type) instead of the legacy warm-paper
palette. Swap the "/" placeholder for the real carved-tile brand lockup,
restyle the toolbar actions as kinpaku primary/secondary buttons, and
recolor the finding overlay from off-brand magenta to vermilion.
Update the global theme-color from #fafafa to #010101 (the sRGB render
of the lacquer ground) so the browser chrome matches the dark site.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Homepage: hero finalist, compact live demo, real picker bar.
Switch the hero to m-01-v2-01, tighten the in-hero demo layout, and replace
the marketing gbar with a shared LiveDemoGbar that mirrors live-browser.js.
Size the bar with max-content so controls are not clipped inside the capsule.
Co-authored-by: Cursor <cursoragent@cursor.com>
* site: migrate /cases/neo-mirai to neo-kinpaku design system
Rebuild the Neo Mirai case-study page on --ks-* tokens: lacquer ground
(drops the off-brand magenta radial spotlight), Alumni Sans Pinstripe
display headings instead of the banned italic serif, gold eyebrow/labels,
gold hairline image frames, kinpaku primary/secondary buttons, and a
lacquer-deep command panel with a gold-bordered code block.
Opt .neon-case-page into the shared kinpaku site-header/footer chrome in
kinpaku-kit.css (per the "add new kinpaku pages to the selector list"
note) so the global header and footer go dark to match the page.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* site: consolidate kinpaku header+footer into one reusable .kinpaku-chrome class
The dark header/footer were not a reusable unit: the header was scoped to
a per-page selector list, the github star pill was home-only, and the
default footer was copy-pasted into four page stylesheets. Pages not on
the lists (like /cases/neo-mirai) fell back to the legacy light chrome.
Collapse all of it into one `.kinpaku-chrome` block in kinpaku-kit.css —
header, github pill, and default footer — and opt every kinpaku page in
via a single body class. Delete the four duplicated per-page footer
blocks and the home-only github pill. The home page keeps its textured
verdigris footer as a deliberate override, raised to body.home-kinpaku
specificity so it wins regardless of import order. Genuinely light pages
(privacy, tutorials) just omit the class.
Fixes on /cases/neo-mirai: footer and github star now render dark/kinpaku
(were legacy-light), and the content sections are wrapped in the .neon-case
container so they sit in header-aligned gutters instead of bleeding to the
viewport edge.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* site: migrate privacy + tutorials to kinpaku via a reusable surface class
These were the last two light pages. Rather than rewrite their per-rule
styling, add a reusable .kinpaku-surface class that remaps the legacy
--color-* / --font-* tokens to kinpaku values at the body scope, so the
existing legacy-token CSS (sub-pages.css prose, the pages' inline styles)
renders dark for free. Same trick docs-kinpaku/slop-kinpaku use per page,
lifted into one shared class. Pair it with .kinpaku-chrome for header +
footer.
privacy + both tutorials pages now carry both classes. Also force the
sub-1.2rem headings (tutorial card titles, prose h1/h2) back to the
upright body face: the legacy display face was italic serif, and the
kinpaku Pinstripe face reads wrong synthesized-italic at small sizes.
No light pages remain.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* site: re-add Tutorials to the /docs sidebar
Tutorials lost its docs placement across two refactors: the Astro docs
rebuild never carried over the sidebar tutorials list the old generated
pages had, and the kinpaku homepage redesign dropped the "Full
walkthrough" link. It survived only via /designing and /live-mode.
Add a "Tutorials" group at the top of the docs sidebar (matching the
command-category styling) linking the index plus all four tutorials,
restoring the old information architecture.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* site: make kinpaku the default — flip legacy :root tokens to dark (phase 1)
Repoint the legacy design tokens in tokens.css from light-mode to kinpaku:
--font-* now reference the --ks-* brand faces (retiring Cormorant/Instrument/
Space Grotesk), surfaces carry dark-lacquer oklch, and --color-accent is gold
instead of magenta. Values mirror the per-page kinpaku remaps.
Every live page already overrides these at its body-class scope, so this
changes the fallback (any classless/new page now renders kinpaku) without
altering existing pages — verified home, designing, slop, live-mode, docs
unchanged, and the deliberate-light demos (slop specimens, home's Aurelia
mock) still render light via their own colors.
First step toward removing the per-page remaps; those become redundant next.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* detector + slop: cream-palette rule, drop everything-centered, polish catalog
- new deterministic cream-palette rule ("claude beige"): flags warm
lightly-tinted off-white page backgrounds; wired into static + browser
engines, with fixture + test
- remove everything-centered rule entirely (no longer in the skill) from
registry, regex analyzer (+ index-offset fix), checkPageLayout, and tests
- catch Instrument Serif in overused-font (regex + OVERUSED_FONTS)
- /slop: reconcile catalog (cream card in, everything-centered out; counts),
and fix demo visuals — visible hairline border, gigantic clipped hero,
more extreme crushed tracking, padded gray-on-color card, uniform-rhythm
monotonous-spacing, long line-length line, elastic-overshoot dialog for
bounce easing, real zooming image for image-hover; flip the demo surface
off warm beige to a cool neutral
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* detector page: add cream-palette fixture to the catalog
Surfaces the new cream/beige palette rule on /detector alongside the
other Color specimens.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* site: shared docs sidebar + tutorial pages join the layout
Extract the /docs section sidebar into a reusable DocsSidebar component
and wire it into all three entry points so the navigation is consistent
across docs index, command pages, and tutorial pages.
site/components/DocsSidebar.astro (new): one source of truth. Loads the
tutorials + skills collections, renders Tutorials → Commands grouped by
category, and highlights the active entry via activeCommand / activeTutorial
props.
site/pages/docs/index.astro: swap the inline sidebar markup for the
component. Drop the "All tutorials" link — the dedicated tutorials
listing page wasn't earning its slot in the rail.
site/layouts/Doc.astro: same swap. Command pages now also see the
Tutorials section above Commands, matching /docs.
site/pages/tutorials/[...slug].astro: rewrite from a standalone page
(custom .tutorial-page wrapper, ad-hoc breadcrumb) to the full
skills-layout shell with DocsSidebar in the left rail. Tutorial content
now reads in the same layout as command reference pages.
site/content/tutorials/brand-vs-product.md (deleted): the skill picks
the register automatically from PRODUCT.md, so a tutorial telling users
to pick it themselves was misleading.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* detector: catch Tailwind warm-light bg utilities in cream-palette
The static engine can't resolve Tailwind classes to computed CSS, so a
`bg-amber-50` on <body> slipped past the cream-palette rule. Add a
class-list fallback that scans body/html for arbitrary `bg-[...]` values
and named warm-light utilities (amber/orange/yellow/stone), each run
through the same isCreamColor test so neutrals and over-saturated shades
drop out. Fixture + test for the class-only case.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* site: drop redundant per-page token remaps (phase 2)
With kinpaku now the :root default, the --color-* / --font-* remap blocks
in docs/slop/designing/live-mode-kinpaku.css re-declared values identical
to :root. Removed them, keeping only the --ks-muted alias (still read by
name in those files) and each page's shell (gradient bg, color, min-height).
home-kinpaku.css keeps its remap: it uses home-specific values (e.g.
--color-charcoal: var(--ks-text), --color-cream: var(--ks-lacquer-raised))
plus the --cat-* gradient overrides, so it is not redundant.
Verified designing (PRODUCT.md viz), slop (specimens stay light), docs,
live-mode unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* site: drop italic from 15 dead editorial-serif heading rules
Audited every font-style: italic in sub-pages.css and main.css against
the live markup. Removed italic from the 15 rules whose selectors don't
appear in any page/component/content/script:
sub-pages.css: docs-home-card-title, docs-category-title,
tutorial-embed-caption, skill-demo-caption, skill-source-card-subtitle,
skill-references-heading, skill-reference-title
main.css: hero-title-combined, hero-tagline-combined, impeccable-title,
loading-state, install-primary-howto .install-path-desc em,
install-howto-steps > li::before, install-step-status, consulting-title
These were dormant remnants of the retired Cormorant italic-serif look —
the kinpaku Pinstripe face renders them as bad synthesized-italic, but
no markup matches the selectors so nothing rendered. Removed only the
font-style declaration; the rest of each rule stays (whole-rule cleanup
is out of scope).
Kept the 5 live selectors (slop-section-heading, tutorial-card-title,
visual-mode-demo-caption, visual-mode-method-name, gallery-card-title)
per the "if they're not used anywhere" condition, plus .prose em (real
emphasis) and .prose blockquote (conventional blockquote italic).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* skill: brand-seed palette.mjs + Setup step to run it
New-brand color now starts from a curated seed color (129 OKLCH seeds)
instead of the model guessing or defaulting to warm-cream. The script
returns one seed + composition guidance (pure-bg architecture, perceptual
text-on-fill, anti-cliché moods, jewel-tone range), with inverse-frequency
hue weighting for fair rainbow exposure and deterministic --from picking.
SKILL.md Setup step 5 makes it run for greenfield projects. Curation
tooling lives in the impeccable-evals repo (tools/palette/).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Remove accidental live mode inject from Base.astro.
The localhost live.js tag was left in the site layout after a dev session and should never ship in the Astro template.
Co-authored-by: Cursor <cursoragent@cursor.com>
* site: dedicated /changelog + /faq, epic v3.5.0 notes, Live Mode → Beta
Split changelog and FAQ out of the homepage into two standalone kinpaku
pages, linked from the footer (and a quiet hint under the Get-started CTA).
/changelog: every release inline (no collapsible), newest first. The
v3.5.0 entry leads with a one-line summary, a real before/after pair from
the GPT-5.5 eval corpus (luxury-hotel brief, skill off vs on), and a stat
row (74% cream-bg, 76% extreme tracking, 90%+ low-contrast — measured
across ~190 samples). Then five scannable bold-led bullets, biggest
takeaway first: per-provider skill compilation, the bias-fix, Live Mode,
the 7 new detector rules, the tighter skill. Before/after JPGs optimized
to ~470KB total (down from ~2.5MB PNGs).
/faq: the six support questions, each deep-linkable.
Live Mode is now Beta everywhere it surfaces: the /live-mode eyebrow
badge and note, the homepage bento tile badge, and the changelog entry.
The historical v3.0 changelog entry stays "Alpha" — accurate to what
shipped then.
Footer trimmed to the four links not already in the top nav (Changelog,
FAQ, Privacy, GitHub).
Version bumped 3.2.0 → 3.5.0 across the three plugin manifests; the
3.2 bias-fix work folds into this release rather than shipping separately.
astro.config.mjs: disable the dev toolbar.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* site: point /design-system hero at the m-01-v2-01 finalist
design-system.css referenced kintsugi-hero-v2.png, an untracked orphan
that was never committed. Repoint it at the committed m-01-v2-01 finalist
so /design-system and the homepage hero share one image, and the page
no longer depends on a file outside the repo. The v2 orphan moved to tmp/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* build: sync harness mirrors + green the prose gate
Rebuild propagates the committed skill source (palette.mjs Setup step,
detector rule updates, brand.md) into the 13 harness output dirs and the
plugin subtree, which had drifted from source.
Also fixes the prose validator, which had been red on six pre-existing
hits across committed files:
- Four em dashes in code comments (Testimonials.astro, LiveDemoGbar.astro,
index.astro) and one in skill/reference/live.md — reworded to colons/commas.
- Two in the slop catalog (an em-dash-overuse specimen and the
marketing-buzzword rule naming "empower"). Those are intentional: the
slop page documents every antipattern by example, so it must contain
them. Exempted site/pages/slop from validateProse rather than neutering
the specimens.
`bun run build` is now green end to end: counts validate, prose passes,
site builds.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* skill: rewrite no-section-fade rule to fix Gemini zero-motion overcorrection
The old rule ("whole-section fade-on-scroll is the saturated AI motion
reflex") drove Gemini to overcorrect into shipping pages with no motion
at all: motion-variety 39% / zero-motion 12% with the skill on, vs
~74-78% variety and ~3% zero-motion without it.
Rewrite keeps the legitimate-stagger carve-out, names the defect at
shape level (one identical entrance on every section) without
enumerating motion primitives, and adds an explicit clause that
suppressing the reflex is never grounds for a static page.
Validated on Gemini 3.5-flash (n=10, luxury-hotel + infra-platform):
motion-variety 39% -> 70%, zero-motion 12% -> 0%, staggered-reveal
stays 0% (reflex not re-inflated).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* release: bump CLI to 2.2.0 and extension to 1.1.0
Both ship the expanded detector: the 7 new rules (cream-palette,
em-dash-overuse, marketing-buzzword, numbered-section-markers,
aphoristic-cadence, broken-image, italic-serif-display) plus
hero-eyebrow-chip, with everything-centered removed. 41 rules total.
The extension settings page already supports toggling them: the rule
list renders from detector/antipatterns.json, grouped by category, and
disabledRules flows through chrome.storage.sync into the scan config,
which detect.js honors by rule id. New rules are toggleable with no UI
change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* release: fix release.mjs for the moved changelog + add CLI/ext entries
The changelog moved from site/pages/index.astro to its own
site/pages/changelog.astro with new markup (cf-version / cf-entry /
cf-items), which left release.mjs reading the wrong file with the old
selectors. All three release commands would have failed at note
extraction. Point it at changelog.astro, match cf-version, and scope
notes to the <ul class="cf-items"> bullet list — that also skips the
lead paragraph, before/after figure, and stat row on the v3.5.0 entry,
keeping release notes to clean bullets.
Add CLI v2.2.0 and Extension v1.1.0 changelog entries (the shared
detector update: 7 new rules, everything-centered removed, 41 total;
plus the extension's per-rule toggles) so release:cli and release:ext
have notes to extract.
Verified extraction for all three labels: v3.5.0 (5 bullets),
CLI v2.2.0 (3), Extension v1.1.0 (2).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: correct dev server port to 4321 and drop stale pnpm-lock
Astro serves on 4321, not 3000 as the docs claimed; update CLAUDE.md,
AGENTS.md, and screenshot-antipatterns.js. Remove the leftover
pnpm-lock.yaml from the Astro migration so Cloudflare's frozen install
uses the maintained, in-sync bun.lock instead of a drifted pnpm lockfile.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* site: rework /designing flow, rhythm, Live Mode mock, and CTA
Restructure the page so iteration reads as the core value, not net-new.
The four loop phases are wrapped in a track with a sticky scroll-spy nav
(Start/Iterate/Polish/Maintain) that pins under the header and highlights
the active phase; the surfaces section (skill/CLI/extension) moves out of
the loop into the post-loop context group so the loop runs uninterrupted.
Fix the iterate split: shared subgrid row tracks so the terminal and the
Live Mode mock align on the same baseline regardless of paragraph length,
wider intro measure (52ch, was a crammed 36ch), and a deeper picker stage
so the context and global bars breathe instead of stacking on the card.
Rebuild the Live Mode mock to mirror the real picker: carved-tile mark plus
Pick / Insert / Detect / DESIGN.md controls on lacquer-deep with the gold
border, and a /impeccable live entry line so the reader knows how to start.
Reframe Start as the hard mode, move h3 subheads off the thin display face
onto Albert Sans, and trim Start so it no longer dominates the loop.
Rework the closing CTA into two standalone raised cards (the bento plinth
made them read as boxes nested in a box), and fix the tutorials copy: there
are three walkthroughs now, and the brand-vs-product tutorial is gone, so
drop it from the CTA and remove the dead lane link to it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* site: reorder Get Started so usage follows setup, link out to more
Move the /impeccable usage examples below the Chrome extension, CLI, and
Stay-updated block. Running a command is the logical next step once the
skill, extension, CLI, and subscriptions are all in place, so the section
now reads install -> set up the extras -> use it. Add a closing "Go deeper"
line linking to the Designing with Impeccable workflow page and the docs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: install compiled per-provider skill variants, not uncompiled source
`npx skills add` (and `impeccable skills install`, which wrapped it) installed
the uncompiled skill/ source verbatim: the skills CLI dedupes discovery by name
and picks skill/SKILL.md first, so installs shipped unresolved {{placeholders}}
and no vendored detector (#168).
- Rename skill/SKILL.md -> skill/SKILL.src.md so the skills CLI's discovery
skips the source and falls through to a compiled .agents variant; update the
build reader, skill-behavior harness, and docs to match.
- Refactor `impeccable skills install` to copy each harness's compiled variant
from the universal bundle (real dirs, no npx skills, no symlink), with
project/global harness detection and a --providers override.
- Fix stale unit tests (replacePlaceholders, readPatterns, transformer
prefix/summary) that asserted removed pre-v3.0 behavior, and wire the three
orphaned test files into `bun run test` so the drift can't recur.
- Split skills-cli.test.js: pure blocks run by default, network blocks move
behind a new `bun run test:cli-e2e`; fix its stale update assertions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* site: default to `npx impeccable skills install`, restore install-method panel
Get Started recommended `npx skills add`, which installs a single shared build
across harnesses. Make our CLI the default (it installs the build compiled for
each harness) and bring back the "Other install methods" disclosure the
neo-kinpaku redesign dropped.
- Homepage: primary command is now `npx impeccable skills install`; a native
<details> panel offers the Claude Code plugin and `npx skills` (caveated as
installing one shared build rather than the per-harness one).
- FAQ: recommend `npx impeccable skills install` to install, `--force` to
reinstall, and note the npx skills shared-build caveat.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* site: reword craft tagline so it doesn't lead with "Shape"
The craft card's tagline began with the word "Shape", which reads like
the name of the sibling /shape command and made the two cards look
swapped (#166). Reword to "Design it, then build it, all in one flow."
No data was actually swapped; this is a copy collision fix.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* skill: rename teach -> init and expand its setup flow
Rename the `/impeccable teach` command to `/impeccable init` across the
skill, site, CLI, and tests. `teach` stays as a deprecated router alias and
/docs/teach + /skills/teach redirect to /docs/init.
Expand the command beyond writing PRODUCT.md/DESIGN.md: the same codebase
crawl now also pre-configures `.impeccable/live/config.json` (Step 6, with
CSP consent) so live mode boots with no first-time detour, and the flow ends
by recommending the best commands to run next from what the scan surfaced
(Step 7).
Fold two items into the unreleased v3.5.0 changelog entry: the init rename
and the brand-seed palette picker. No version bump.
Regenerates all harness skill output dirs and the _redirects file.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs: lead README install + usage with the CLI installer
Add `npx impeccable skills install` as the recommended install option and
update the Usage section to the `/impeccable <command>` form, dropping the
nonexistent `/normalize` example.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(skill-behavior): swap to production-tier models (sonnet + gpt-5.5)
Replace the cheap-tier default lineup (claude-haiku-4-5, gpt-5.4-mini) with
production-tier models (claude-sonnet-4-6, gpt-5.5) so the skill-behavior
suite reflects what users actually run. gemini stays on flash-lite.
Sync the docs (CLAUDE.md, AGENTS.md, tests/skill-behavior/README.md): new
model names, cost estimate raised to ~$0.50-1.50/sweep, and the old 21-22/24
baseline reframed as previous-cheap-tier history pending re-measurement on
the new lineup.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat: self-updating skill via boot-time version check
context.mjs now polls a new lightweight /api/version endpoint at most once
per day (cached globally in ~/.impeccable) and appends an UPDATE_AVAILABLE
directive when a newer skill version has shipped, prompting the agent to
offer `npx impeccable skills update`. Best-effort and silent on any failure;
asks before updating; suppresses re-prompts for a declined version for a
week. Opt out with IMPECCABLE_NO_UPDATE_CHECK=1.
- skill/scripts/context.mjs: version read, throttle + anti-nag cache, directive
- scripts/build.js + _redirects: /api/version endpoint (from plugin.json version)
- skill/SKILL.src.md: document the UPDATE_AVAILABLE boot branch
- tests/context.test.mjs: coverage for cached/newer/suppressed/opt-out paths
- changelog: v3.5.0 entry
- synced harness skill dirs via bun run build
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test: cover the self-update path (network + LLM behavior)
context.test.mjs: add a localhost stub-server integration test for the live
fetch path (poll /api/version, cache a newer version, stay silent on
same-or-older, fail silent + stamp lastCheck when unreachable). Runs against
127.0.0.1 only, never the real site; uses async spawn so the in-process stub
isn't deadlocked by spawnSync blocking the event loop.
skill-behavior: add scenario 9 asserting the agent surfaces UPDATE_AVAILABLE
but never auto-runs `npx impeccable skills update` without asking. New
prepareWorkspace `skillVersion` copy-mode (so context.mjs has a SKILL.md to
version-check), env threading through runTurn -> execBash, and bash-output
capture to prove the agent actually received the directive. Passed on
claude-sonnet-4-6, gpt-5.5, and gemini-3.1-flash-lite.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Previously the main `live-server integration` describe block spawned its
shared server against REPO_ROOT, so its session journals/snapshots
(a1b2c3d4-dc, aa11bb22, sse-test, test-e2e-1) were written into the
real repo's `.impeccable/live/sessions/`. On the next `npx impeccable
live` run, restorePendingEventsFromStore replayed those into the poll
queue, surfacing as synthetic test events to the agent.
Run the shared server against a mkdtempSync tmpdir, seed a minimal
package.json so the /source endpoint test still passes, and route the
inline journal/snapshot reads (and the live-complete.mjs call) through
server.cwd.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The `import.meta.url === \`file://\${process.argv[1]}\`` guard at the
bottom of critique-storage.mjs silently failed on Windows: Node sets
import.meta.url to file:///D:/... (forward slashes) but process.argv[1]
is D:\... (backslashes), so the string compare returns false, main()
never runs, and the script exits 0 with no output. The OpenCode reporter
saw "/impeccable critique" skip the snapshot save with no error.
Switch to pathToFileURL(process.argv[1]).href, the standard cross-
platform pattern already used everywhere else in the repo.
Adds three CLI subprocess tests so future regressions of this guard
are caught even on macOS/Linux CI.
Fixes#155.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>