mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-18 00:56:30 +03:00
61f37ccfb8d8619dae10e60aee15f793cb4834fc
87
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
61f37ccfb8 |
Drop the progressive benchmark, remove dead wrap scaffolding
Review fallout from removing progressive publication. The Live benchmark existed to compare atomic against progressive delivery: compareModelBackedReports measures goToFirstVariantMs improvement of one over the other. With progressive gone it measures nothing against nothing. Worse, benchmark-live.mjs still passed `progressive` to bootFixtureSession, which no longer accepts it, so `--delivery progressive` was silently ignored and would have emitted reports labeled progressive that actually ran atomic. Silent wrong data is worse than a crash. It was built for progressive, so it goes with progressive: benchmark-live.mjs, its lib, its test, and the bench:live script. If an atomic latency baseline is wanted later, that is a smaller thing built on purpose. live-wrap.mjs: sourceOriginalLines was assigned and never read. Both found by review bots on #381 (Copilot). Assisted-by: Claude (claude-halva-eap) via Claude Code |
||
|
|
55b297d7fc |
Live: polling rework, source locks, preflight scaffolding, Vue previews
Carved out of #371, minus progressive publication. Everything here works against real project source the way main's Live already does: the agent writes variants into the file the browser loaded, HMR fires, Accept promotes and carbonizes. Nothing is staged anywhere. Poll lanes. Events now carry an explicit priority: accept/discard/exit ahead of manual_edit_apply/steer/carbonize_cleanup ahead of generate. A long generate can no longer sit in front of the Accept the user just clicked. leaseEvent claims its lease before awaiting, so a slow prepare cannot hand the same event to two pollers. Source locks. A per-file mutex around every accept and discard path, keyed on a digest of the absolute path. Staleness is decided by owner-pid liveness rather than mtime, so a wedged lock clears when its owner dies instead of after an arbitrary timeout, and a slow-but-live accept is never stolen from. Only the owning process can release a lock. Preflight scaffolding. The server runs live-wrap (or live-insert) before the poll returns and hands the result back as event.scaffold. That walk is measured at ~7.6s on a large repo; moving it off the agent's critical path removes a deterministic tool round trip without touching the generated design. Falls back cleanly to the agent running the helper itself. Vue previews. previewMode: "vue-component" for Nuxt/Vue targets, matching the existing Svelte component path: variants compile as real SFCs from a dev-only directory so the route is never rewritten during generation, and Vite mounts them without invalidating page state. Accept is the only route write. Includes a Vue attr tokenizer that normalizes shorthand bindings (@x, :x, #x) to their canonical forms. Accept hardening. Every thrown failure now returns mode: 'error' rather than an ambiguous unhandled result, so a real failure is never classified as a deliberate manual handoff and silently dropped. The marker search skips node_modules/.git/dist/build/.impeccable. Shared CLI arg parsing extracted to scripts/lib/cli-args.mjs. Assisted-by: Claude (claude-halva-eap) via Claude Code |
||
|
|
c654acb005 |
Rip out the dead isolated-preview mode and the private repo's job
Comparing this branch's live against main's turned up two whole features that never made sense here. -2,466 lines. 1. The isolated source-artifact preview was never switched on. `scaffoldSourceArtifactSession` is only reachable via live-wrap's `--isolated`, and nothing passes it: not the server's preflight, not live.md, nothing. Proved it end-to-end — the default wrap writes markers straight into real source and creates no previews/ session. So the mode was wired through three modules, carried its own accept/discard branches, browser branches, server metadata resolution, preview-mode classifier entry, and test suites, and none of it could run. Worse, live.md documented it as the active path and told the agent "The true source is only the publisher's hash fence and must remain byte-identical until Accept." That is false: the wrapper lands in source at scaffold time and each revision rewrites it. An agent following that sentence believes source is protected when it isn't, and the leftover artifacts are what made accept resolve the wrong file in the first real run. live.md now describes what actually happens, including that markers are visible in source until Accept or Discard. Removed: source-artifact.mjs, --isolated, the preflight's isolated option, the accept/discard branches, four dead browser branches, the server's previews/ resolution, the classifier entry, and their tests. Kept the previews/ gitignore pattern: an ignore line for a directory that cannot exist is free, and a test pins it. 2. Quality judging belongs to the private evals repo, which says so. runner/live/README.md there is explicit: the public repo owns protocol correctness, framework coverage, timing, source commit, recovery, and a rubric-free evidence bundle; the private repo owns the task corpus, baselines, comparative judges, and release-quality decisions — "Do not add quality rubrics, competitor comparisons, or broad fixture corpora to the public Live benchmark." This branch added exactly those: an LLM judge scoring 1-10 on "off-brand, generic-AI" (live-rendered-quality.mjs, judge-live-rendered.mjs), a cross-provider comparison with a BRAND_CONTRACT rubric (live-provider-benchmark .mjs, benchmark-live-providers.mjs), and a brand-fidelity fixture corpus. All removed, with bench:live:providers and their suite entries. Also removed tests/framework-fixtures/README.md's "External quality-eval fixtures" section: it documented a bench:live workflow using --fixture-dir, --agent=codex, --action and --evidence-bundle, none of which benchmark-live.mjs implements, plus an evidenceCapture block nothing reads. Kept: timing benchmarks (the public repo's half of that boundary), progressive publication, the source lock, poll lanes, and Nuxt/Vue component previews. Coverage note: deleting the isolated suites took the only tests for `source_locked` classification with them, so the plain wrapper path — now the only non-component preview — gets equivalent accept and discard coverage. Both new tests fail if mode:'error' is removed. Prepared with AI assistance under maintainer direction. Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
c7b67b3832 |
Drop the live generator subagent; fix the artifact decoy that broke accept
The first real Claude Code Live run failed, and the subagent was not the cause. Root cause: progressive publication stages each revision as `.impeccable/live/artifacts/<id>-r<n>.<source-ext>`, nothing ever deleted them, and findSessionFile's walker skipped only node_modules/.git/dist/build. It searches src, app, pages, ... then `.`; a project whose source is not under one of those (this repo's own site lives in site/pages/) falls through to the `.` walk, where dot-directories sort before letters. So accept found the artifact instead of the real file. Two outcomes, both reproduced: where isGeneratedFile returns true it declines with mode: 'fallback' (what the run hit, after which the agent hand-carbonized several hundred lines across three stylesheets, including unrequested drive-by edits); where it returns false, accept writes the variant into the throwaway artifact and reports handled: true while real source never changes. The E2E suite could not have caught this. Every fixture puts source under `src/`, which is searched before the `.` walk can reach `.impeccable`. Five framework fixtures and three progressive scenarios pass because of fixture layout, not because the path works. I read that as evidence and shouldn't have. - Never search `.impeccable`: it is Impeccable's own state, never project source. - Retire a session's staged artifacts on accept/discard, so they cannot outlive the session and become a decoy for anything else that walks the tree. - Regression tests use a site/pages layout with artifacts present. All three fail against the previous code. Generator subagent removed, on both harnesses: The parent must hand-compress the design system into the handoff, and compression is lossy. Measured on the real run: a 6,826-char handoff carrying exactly one token reference, after the parent had itself read kinpaku-tokens.css. The subagent then spent 3 of its first 9 turns hunting DESIGN.md, gave up, and emitted 0 var(--token) uses and 22 raw oklch literals — violating its own spec's "Never invent raw colors when tokens exist" — including a 1:1 gold-on-gold contrast bug. Isolation is not a benefit here; knowing the design system is the job. Generation stays in the main thread, which already holds the tokens and writes them from the first byte, so carbonize is a move rather than a translation. Copy edits keep their subagent: applying a known set of ops to a named file is self-contained, so an isolated context costs nothing. That is the line. Progressive delivery stays for Codex and Claude Code, main-thread driven. Claude Code keeps the full benefit because its poll is a background task. Codex's poll blocks the foreground, so with no subagent the user sees variant 1 early via HMR but cannot accept it until the trio finishes; that is the cost of the simplification and it is worth naming. Prepared with AI assistance under maintainer direction. Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
e2ef633b95 |
Finish the failed-accept classification my last commit only half did
All three new findings are the same root cause, and it is my incomplete fix:
operationFailure only covered results built from a *thrown* error. Two paths it
missed:
- Two catches wrote the failure result as a multi-line literal, so the
single-line replace skipped them. The Vue accept catch was still bare, exactly
as reported; the Svelte one too, though its failures happened to be caught by
completion.mjs's Svelte-only special case.
- The accept implementations also *return* `{handled: false, error}` for their own
checks (variant missing, template empty, original text ambiguous). Those never
throw, so no catch ran and no `mode` was set.
Both layers now agree, because each is reachable on its own:
- live-accept marks any unhandled preview-path result via markPreviewFailure,
keyed on `previewMode` — a clean discriminator, since only the preview branches
set it and a plain wrapper never does. This is what the agent reads:
reference/live.md routes on `mode`, so without it the agent was told "read
file, find markers, edit" for a preview that has no markers in source.
- completion.mjs replaces its arbitrary svelte-component special case with the
set of preview modes whose variants live outside the user's source. That case
existed for precisely this reason; Vue and source-artifact were simply never
added, so the identical failure on those paths acknowledged as success.
The plain wrapper keeps its manual handoff, which is the one shape with editable
markers in source. Both deliberate handoffs (mode: 'fallback' and markers not
found) still classify as agent_done, now pinned by a test so the generalization
cannot swallow them.
Prepared with AI assistance under maintainer direction.
Co-Authored-By: Claude <noreply@anthropic.com>
|
||
|
|
6cbb7ce8d1 |
Classify failed accepts as errors, and fix parallel lane race/all misuse
Two of the three new findings, plus the bug that chasing them exposed in my own earlier fix. The third is mitigated rather than broken; details below. Failed accepts reported success: live/completion.mjs only classifies a result as `error` when it carries `mode: 'error'`. Everything else unhandled falls through to `agent_done` with an ok ack, which is deliberate for the documented fallback paths (two tests pin it) but wrong for a real failure. So `accept_receipt_conflict` reported success, and reference/live.md's `handled: false` without `mode` bullet told the agent to "read file, find markers, edit" — hand-applying a second accept on top of the one the receipt already recorded. The same hole swallowed `source_locked`, which is mine: the earlier commit made lock contention return clean JSON so the agent could retry, but the classifier turned that failure into agent_done/ok, so the accept was dequeued and silently lost. Mark genuine failures with `mode: 'error'` through one `operationFailure` helper, and give live.md a `mode: "error"` bullet with per-error guidance: retry the same command on `source_locked`, never hand-edit, and on a receipt conflict report what the session actually resolved to. The deliberate fallback and markers-not-found handoffs stay untouched. parallel-compact lane orchestration: `Promise.race` settles on the first *settlement*, so one lane failing fast rejected the whole first-variant step while two lanes were still on their way to succeeding. `Promise.any` now takes the first success and only a total wipeout is fatal, reporting every lane's reason. The tail step's `Promise.all` surfaced a raw lane error non-deterministically; `Promise.allSettled` now reports how many lanes failed and why. Added a `requestImpl` seam so lane orchestration is testable without a provider key. Not a defect: the browser releasing Accept before the source write. That is the intended optimistic design, and it is safe because poll-lanes ranks accept at priority 0 against generate at 2, so a queued accept is always leased before a generate the user queues afterwards, even if the generate arrived first. Its source write lands inside the poll script before the next generate preflights. That invariant is load-bearing and had no tests at all; poll-lanes.mjs now has a suite covering it plus lease and type filtering. Prepared with AI assistance under maintainer direction. Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
529184bbe4 |
Fix inset-order detection, the unlocked artifact discard, and stray boolean flags
Three of the four open review findings. The fourth is declined below. - The inset-stripe scan only matched layers starting with `inset`, but the keyword is order-independent: `box-shadow: 4px 0 0 var(--brand-accent) inset` paints the same stripe and was silently missed. Strip the keyword wherever it sits, but only as a standalone token, so a color like var(--inset-accent) is not mangled into `var(-- -accent)` and quietly reclassified as neutral. The fixture now covers both orders plus that token, and a trailing-inset neutral still passes. - The source-artifact discard deleted the preview without the source lock, unlike every other discard path. Take the lock. Narrower than reported, though: the server journals `discard_requested` as a fenced phase before live-accept runs and the publisher checks it three times, so a publish could never land on a discarded session. What this actually prevents is deleting the artifact under a publisher mid-critical-section, turning a clean stale_generation_epoch into an ENOENT crash. - benchmark-live-providers.mjs still compared `--headed` and `--skip-cleanup-control` against a boolean sentinel, so the `=true` spelling silently did nothing. My gap: I introduced boolFlag and converted benchmark-live.mjs but not this one. skipCleanupControl is now read once rather than twice, so the two call sites cannot drift. Declined: tightening the selector guard that skips `active` / `current` / `selected` tokens. It does cause false negatives on names like `.selected-feature`, but the rule's contract makes selection and focus indicators its one exception, and `.active-tab` / `.current-step` / `.selected-row` are syntactically identical to `.selected-feature`. No regex separates them, so tightening the guard trades missed stripes for false positives on exactly the case the rule exempts. The conservative skip is the intended behavior. Prepared with AI assistance under maintainer direction. Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
fc620b9620 |
Bring Live progressive delivery and the generator subagent to Claude Code
Almost none of this branch's Live work was actually Codex-specific. The publisher,
the fences, the source locks and the browser's partial-arrival UI are plain node
and DOM with zero provider references, and the progressive E2E already passes on
five frameworks driven by a non-Codex agent. The Codex-only part was policy prose
and one frontmatter line, so Claude Code shipped the progressive browser UI it
could never trigger.
Progressive delivery, Codex and Claude Code:
- Add a `live-progressive` capability tag and opt codex, agents, and claude-code
in. A provider block takes one tag, so naming harnesses would have meant
duplicating the recipe per tag; a capability reads better than a provider list
anyway. Cursor and everyone else keep the atomic path until their poll loop is
known not to stall on the extra publish calls.
- Claude Code publishes variant 1 as soon as it validates rather than waiting to
write the whole trio in one edit. Nothing about the arrival path needed
changing: the publisher writes, framework HMR pushes, and the browser's
MutationObserver counts variants. The parent conversation was never in that
path, which is why Claude Code's lack of subagent progress streaming does not
matter here.
Generator subagent:
- Drop `providers: codex` from impeccable-live-generator. The build already maps
its frontmatter correctly for Claude Code, and impeccable-manual-edit-applier
has shipped to .claude/agents/ this way all along.
- The reason differs per harness, so the reference says so: Codex delegates to
unblock a foreground poll, Claude Code delegates to keep a long session's
screenshots and variant CSS out of the main context. Follows the existing
manual-edit-applier convention: both agent names, and an inline fallback when
native subagents are unavailable.
Fixes found on the way:
- The two publish commands hardcoded `.agents/skills/impeccable/scripts/` while
the other thirteen commands in live.md use {{scripts_path}}. Correct only for
the Codex repo-skills bundle; it would have pointed Claude Code at a directory
its install never creates. The shipped .codex variant was already internally
inconsistent. Now covered by a test.
- `--agent=codex` resolved to the canned fake agent, because the flag parsed as
`x === 'llm' ? 'llm' : 'fake'`. The private evals Live runner passes exactly
that, so a real-harness run would have scored deterministic stub variants and
reported them as Codex output. Unknown values for --agent, --scenario and
--delivery now fail loudly.
- live-reference tests now compile with each provider's real providerTags instead
of hand-written lists, so a providers.js misconfiguration fails in tests rather
than shipping.
Verified: progressive E2E green on vite8-react-plain against a real Vite server
and Chromium; every provider variant's publish and poll paths now agree; Cursor
and Gemini still compile to atomic only.
Prepared with AI assistance under maintainer direction.
Co-Authored-By: Claude <noreply@anthropic.com>
|
||
|
|
f148496f67 |
Let hooks ignore-value scope a rule to files, and stop churning the config
Fallout from suppressing the overlay's font-size findings: the narrowest exception detector.ignoreValues supports was unreachable from the path the hook tells the model to use, so the guidance steered to the blunt instrument instead. - hook-admin's ignore-value now takes --file / --files / --file= / --files=, matching `impeccable ignores add-value`, which already had them. Without it the only file-scoped option was ignore-file, which silences every rule for a path permanently, including rules not yet written. - A bare wildcard value is now refused with a message pointing at either --file or ignore-rule. Previously `ignore-value <rule> "*"` quietly wrote a project-wide suppression from a single file's finding. - ignore-value keyed entries on rule+value only, so a second scope for the same rule overwrote the first instead of coexisting. Key on the file scope too. - An unknown flag folded into the value: `ignore-value overused-font Inter --shard` stored "inter --shard", matched nothing, and reported success. Reject it, as the sibling command does. Config churn: normalizeIgnoreValueEntries runs on every write and emitted keys as rule, value, files, reason, createdAt while the config on disk uses createdAt before reason. Any edit therefore rewrote every untouched entry (35 churned lines for a one-line change). Pin the canonical order in both copies of the normalizer and in ignores.mjs, and add a test that the two copies cannot drift apart. Also point the hook's own footer and reference/hooks.md at the file-scoped form first, and say plainly what ignore-file costs. Prepared with AI assistance under maintainer direction. Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
60c4fa25db |
Stop telling users a busy agent is disconnected
The agent-poll indicator tracks whether a poll is parked, which is the right signal for "can steering reach the agent right now" and is why the flag itself is left alone. But it goes quiet for two different reasons, and both got the same copy: "Agent disconnected - run live-poll.mjs to connect". Under the one-shot foreground polling that live.md calls the primary contract, no poll is parked while the agent works, so the second reason is every normal generation. For its whole duration the bar told the user a healthy session was broken and advised them to start a poll loop that was already running. Pick the copy from the live state, which the browser already tracks: GENERATING and SAVING mean the agent holds work it was handed, so say it is working. Every other state with no parked poll keeps the original, actionable wording. The aria-label carries the same distinction, since the tooltip is mouse-only. The text is derived at read time rather than cached, because the live state moves between the 5s status polls and a finished generation would otherwise keep reading "Agent is working" until the next one landed. Deriving it also keeps the read out of setLiveState, which runs long before agentPollingConnected's declaration and would hit its temporal dead zone. Prepared with AI assistance under maintainer direction. Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
4e381305e1 |
Fix source-safety, detector, and lock defects in Live polling work
Addresses the review findings on #371, plus several the bots did not catch. All fixes have regression coverage that fails on the prior code. Source corruption: - Vue accept dropped valueless root attrs (disabled, v-cloak) and, worse, rewrote @click="x" as a literal click="x" DOM attribute, because the attr parser was name-anchored and skipped the sigil. Tokenize the whole Vue attr grammar and normalize shorthands so accept round-trips directives. - --variant was interpolated unescaped into a RegExp, so --variant '.*' matched the original block first and reported a successful accept while silently restoring the original. Validate against the digits pattern the browser and the /events schema already enforce. - --id reached path.join unvalidated, so --id ../../../../etc/evil wrote and read receipts outside the project. Hoist the existing safeSessionId check into impeccable-paths and apply it at every id-to-path sink. Accept/lock correctness: - Plain HTML/JSX accept and discard did not catch SOURCE_LOCKED, so contention exited non-zero with empty stdout and the agent got no JSON to retry on. - Lock staleness was mtime-only and never read the pid it records: a holder whose critical section outran 60s had its live lock swept, admitting a second writer to the same file, while a crashed holder blocked accepts for a full 60s. Decide staleness by owner liveness, and release only our own lock. Detector: - isNeutralColor only parses computed color forms, so routing authored CSS through it reported inset 4px 0 0 #000 / black / #e5e7eb as chromatic side-tab stripes. Add an authored-color neutrality test covering hex and named neutrals; the fixture had no literal-color cases at all. - Rule line numbers were off by one for every rule after the first, and commented-out CSS was scanned as live rules. Server: - An error reply carries no sourceEventType, and inferSourceEventType returned undefined, which acknowledgePendingEvent treats as a wildcard: a stale generate worker's failure consumed the user's queued Accept, which then reached no agent and left the browser in SAVING forever. - The generate preflight spawned live-wrap.mjs synchronously inside the request handler, freezing the single-threaded server for the whole scaffold (~7.6s measured on this repo, 15s ceiling) and stalling Accept/Discard/SSE. Make it async, claiming the lease before the first await so no event double-delivers. - Every browser checkpoint was echoed back as variant_progress, so a Tune slider drag remounted the preview under the user's cursor and latched the *_reviewable phases from the wrong trigger. Gate on the reason. Cleanup: - Collapse four divergent benchmark argv parsers into scripts/lib/cli-args.mjs. Three silently misread flags: --iterations 20 benchmarked 5, --agent llm ran the fake agent, --median-target=0.4 used the default threshold. - Drop a snapshot cache this branch made write-only (it grew per session for the server's lifetime and was never read), a dead exported reconcile helper, and the unused deferReply branch. Prepared with AI assistance under maintainer direction. Co-Authored-By: Claude <noreply@anthropic.com> |
||
|
|
c6ac34b929 |
Improve Live polling responsiveness and reliability
Restore foreground/background polling as the primary harness architecture, add progressive publication and framework-safe previews, and harden quality and regression coverage. The experimental app-server runtime is intentionally excluded.\n\nPrepared with AI assistance under maintainer direction. |
||
|
|
da99645a58 |
Add OpenAI plugin submission bundle (#363)
* Add OpenAI plugin submission bundle Build a Codex-native OpenAI plugin with bundled hooks, public listing metadata, submission guidance, privacy coverage, and regression tests. AI assistance: OpenAI Codex prepared and validated these changes under maintainer direction. * Fix provider script command rendering Replace heuristic rewrites across executable scripts with one explicit provider marker, render pinned shortcuts per target harness, and remove the personal email from the public publisher manifest. Addresses automated review feedback on PR #363. AI assistance: OpenAI Codex prepared and validated these changes under maintainer direction. |
||
|
|
51e5af258e |
Expand init to capture positioning, conversion, and proof context (#315)
* Add positioning and conversion questions to init flow Expand init.md so PRODUCT.md captures audience splits, positioning, and brand-register conversion/proof context before design work starts. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix init over-inference by raising the evidence bar for skipping questions. Sparse repos were letting the model treat weak guesses as settled answers; Step 3 now asks unless the codebase provides strong, explicit evidence. Co-authored-by: Cursor <cursoragent@cursor.com> * Improve init interview order and PRODUCT.md proof output shape. Ask positioning in round 1, actively collect proof assets, and give Proof & conversion a plain bullet skeleton so generated PRODUCT.md stays lean. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix init interview bundling and write-time padding, verified via harness runs Co-authored-by: Cursor <cursoragent@cursor.com> * Revert init reference follow-up rule to advisory wording on line 88 Co-authored-by: Cursor <cursoragent@cursor.com> * Tighten init interview rules after harness runs: split register, options, prose Settle split register before brand-only questions, require standalone emotions and confirmed secondary audiences, forbid compound options, and keep PRODUCT.md bold minimal. Co-authored-by: Cursor <cursoragent@cursor.com> * Ask brand-register init questions in magazine-editor voice, no skill jargon Co-authored-by: Cursor <cursoragent@cursor.com> * Fix init chat fallback to ask one question at a time When no structured question tool exists, init should ask in chat with lettered options and wait for each answer instead of dumping a list. Co-authored-by: Cursor <cursoragent@cursor.com> * Resolve init review comments: split purpose question, gate template section Purpose and success are now separate questions, and docs-stated purpose is framed as a hypothesis below the strong-evidence bar rather than a competing always-ask rule. The PRODUCT.md template now tells product register to omit the Conversion & proof section including its heading. Co-authored-by: Cursor <cursoragent@cursor.com> * Keep belief-sequence question out of skill jargon Ask what visitors must believe in plain words; map the answer to the template belief ladder in a parenthetical instead of leading with the term. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Abdul Wahab <abdulwahab@Abduls-MacBook-Pro-2.local> |
||
|
|
f40e2f8f0a |
Add mechanical pre-scan for typeset and layout (#345)
* Add mechanical pre-scan for typeset and layout commands. Introduce --scope filtering, layout/type rule scopes, DESIGN.md font-size validation, and pre-scan steps in the skill references so agents run detect before LLM judgment. Fixes #149 Co-authored-by: Cursor <cursoragent@cursor.com> * Add isolated sub-agent orchestration for typeset and layout pre-scans. Run the mechanical detector and visual assessment in parallel sub-agents so deterministic findings cannot anchor LLM judgment, matching the critique pattern Paul requested on PR #345. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix: reject bare --scope so detect never scans unscoped by mistake. When --scope had no value, the CLI dropped the flag and ran a full scan instead of failing, which could silently use the wrong rule set during typeset/layout pre-scans. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix: require both typeset and layout assessments in sub-agents. Close a loophole where agents ran only the mechanical pre-scan inline by interpreting "running both" as permitting one inline assessment. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Abdul Wahab <abdulwahab@Abduls-MacBook-Pro-2.local> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
c11cc7b58c |
Route native projects to native command variants (audit, adapt) (#357)
* Route native projects to native command variants for audit and adapt Follow-up to #269. The web audit.md and adapt.md carried "translate this yourself" Platform notes, so a native invocation paid for the full web file (~1.8k / ~2.6k tokens, mostly inapplicable) and did error-prone run-time translation. Authored with AI assistance (Claude Code) under maintainer direction. - New reference/audit.native.md and reference/adapt.native.md: authored native content (VoiceOver/TalkBack, platform conformance, adaptivity dimensions; phone-to-tablet, platform-to-platform, web-to-native strategies). One variant per command covers ios, android, and adaptive; per-OS specifics stay in the platform refs Setup loads regardless. - SKILL.src.md: Commands table lists the variants; Setup step 2 reads the variant instead of the web file when the platform is native. - audit.md / adapt.md: Platform sections replaced with a one-line web-only guard pointing at the variant. - animate.md / layout.md: Platform sections deleted; the Motion and Layout sections of the already-loaded platform refs carry that content. Web users now pay zero tokens for the platform axis in these files. - Skill-behavior scenario 15 pins the route-instead behavior (passes live on claude-sonnet-4-6); CLAUDE.md documents the variant convention. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Phrase command-reference routing as one rule, not rule-plus-exception Copilot review catch: step 2 said "MUST read reference/<command>.md" and then carved out the native variant, which invites loading both files. Now a single rule: read the web reference or the table's native variant, one file, not both. Scenario 15 re-verified live. Applied with AI assistance (Claude Code) under maintainer direction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Anchor native runs in animate/layout, drop loaded-refs assumption Review-thread fixes, applied with AI assistance (Claude Code) under maintainer direction: - Greptile: deleting the animate/layout Platform sections left native runs alone with web tooling instructions (CSS keyframes, GSAP, Grid, clamp()). Restore a one-line anchor in each pointing at the loaded platform reference's Motion / Layout section (~20 tokens, not the old restatements). - Bugbot: audit.native.md and adapt.native.md asserted the platform refs were "already loaded in Setup", but the command reference loads at step 2, before step 5. Now they instruct: read the platform reference first if Setup hasn't already. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Carry the native-variant rule into routing rules 2 and 3 Bugbot catch: Setup step 2 routed native projects to the variant, but routing rules 2 and 3 (the operative text at command time) still said to load the generic reference file. Both now reference the same one-file variant rule. Applied with AI assistance (Claude Code) under maintainer direction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Point animate/layout native anchors at the files, not "loaded" refs Bugbot catch, same class as the variant wording fix: the anchor lines said "the loaded platform reference" but command files load at step 2, before the platform refs at step 5. Both anchors now name the files and instruct reading them first if Setup hasn't already. Applied with AI assistance (Claude Code) under maintainer direction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
3e38e595c7 |
Add platform axis (web / ios / android / adaptive) (#269)
* Add a platform axis (web / ios / android / adaptive) to the skill Orthogonal to register: register decides whether design IS or SERVES the product; platform decides the delivery target and which native conventions apply. Set `## Platform` in PRODUCT.md; a missing field defaults to `web`, so legacy projects are unaffected. - extractPlatform() in skill/scripts/context.mjs (mirrors extractRegister); the CLI appends a NEXT STEP directive to read the native reference(s). `adaptive` (Flutter / RN / KMP shipping both iOS and Android) loads both ios.md and android.md. - New reference/ios.md (Apple HIG distilled) and reference/android.md (Material 3 distilled); reference/web.md is a thin pointer. The native refs frame register's role as narrow: platform conformance is the bar, brand lives in the expressive layer the platform gives you, never by breaking the rails. - Setup step 5 loads the native reference(s) when platform is native. Live mode and the detect CLI stay web-only, gated off ios/android/adaptive. - init asks platform right after register; adapt/audit/animate/layout carry short platform divergence notes; all secondary spots thread `adaptive`. - a11y stays in audit.md (loading it at design time makes output timid), so the native refs carry no Accessibility section; audit.md's Platform section owns native a11y. - Tests: extractPlatform unit coverage + skill-behavior scenario 10 (PRODUCT.md platform ios -> agent loads ios.md). Source-first: only skill/, scripts/, tests/, CLAUDE.md, NOTICE.md, the changelog and version are committed; the sync workflow regenerates the provider trees and ./plugin on merge. ios.md / android.md are distilled from the MIT-licensed ehmo/platform-design-skills; attribution in NOTICE.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Address review: gate web tools on native platforms, drop version churn Maintainer-review fixes applied with AI assistance (Claude Code), on top of the rebased platform-axis commit: - Design hook (post-edit and Cursor pre-edit) now resolves the project platform via loadContext + extractPlatform and skips its web rule scan for ios / android / adaptive projects, so React Native / Flutter code never draws web-shaped findings (new hook-lib resolveProjectPlatform / isNativePlatform helpers, covered by unit and subprocess tests). - context.mjs CLI warns on an unrecognized ## Platform value (e.g. a toolchain name like `flutter`) instead of silently defaulting to web; extractRegister / extractPlatform now share extractSectionValue. - Removed reference/web.md: nothing loaded it; CLAUDE.md carries the "web has no extra rulebook" explanation. - init.md: skip live-mode config (Step 6) for native platforms; note the per-app PRODUCT.md pattern for repos shipping web + native. - android.md: Material-everywhere apps that also ship on iPhone still owe iOS OS guarantees (safe areas, Reduce Motion, edge-swipe back). - ios.md: reworded a design-time line that framed Dynamic Type as an accessibility check (a11y stays owned by audit.md). - Renumbered the new skill-behavior scenario to 14 after main's 10-13; updated CLAUDE.md scenario list; added android + unrecognized-value CLI test cases. - No version or changelog changes: versioning happens at release time. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Tighten platform reference prose Editorial pass on the platform-axis text, applied with AI assistance (Claude Code) under maintainer direction: - ios.md / android.md rewritten to house style: single-line paragraphs (no hard wraps), one-sentence scope intro, deduplicated intro/slop-test, register-compression down to two sentences. In-file attribution paragraphs removed (NOTICE.md owns attribution); "read on top of the register reference" cruft removed (SKILL step 5 and the context.mjs directive already say it). Bans sections dropped: they restated the rules above them; the two additive items (tab-bar overload, hover-dependent affordances) folded into rules. ~40% smaller each. - Sub-command Platform sections (adapt, audit, animate, layout), SKILL step 5, init.md platform prose, and the context.mjs directive trimmed the same way. Build (prose validators, counts) and both test runners green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Treat an empty PRODUCT.md section as absent, not the next heading Copilot review catch: extractSectionValue read the next `## ...` heading as the section value when a field was left empty, which made the CLI warn "value `## Product Purpose` is not recognized". Stop at the next heading and return null instead. Regression tests for extractPlatform, extractRegister, and the CLI warning path. Applied with AI assistance (Claude Code) under maintainer direction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Only read a token list of both native targets as adaptive Bugbot catch: after the exact platform tokens failed, any Platform line containing the words ios and android was classified adaptive, so negated or explanatory prose ("web only, not ios or android") silently loaded both native refs and skipped the hook, with no warning. The combo parse now accepts only list separators and the two platform words; anything else falls through to the CLI's unrecognized-value WARNING. Regression tests added. Applied with AI assistance (Claude Code) under maintainer direction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Paul Bakaus <paul.bakaus@gmail.com> |
||
|
|
1a46353b29 |
Don't force init on scoped commands when PRODUCT.md is missing (#277)
* Don't force init on scoped commands when PRODUCT.md is missing Setup step 1 told the agent: "If it reports NO_PRODUCT_MD, stop and follow reference/init.md before doing anything else." For a project with no PRODUCT.md, that turned every scoped request (polish, critique, audit, layout, ...) into a full from-scratch init detour. The user asks to polish one button and the skill instead starts writing PRODUCT.md from the beginning. Faced with that gate, agents also frequently abandon the command and do an ad-hoc pass without loading the command reference. Make the gate command-aware. A missing PRODUCT.md still routes into init for the from-scratch build flows where captured product context is the point (init, craft, shape). For any other command, a scoped request against existing code, the code is the context: proceed with the requested command, infer the register from the surface in focus, and offer /impeccable init once as a suggestion rather than a blocker. - skill/SKILL.src.md: rewrite the step 1 NO_PRODUCT_MD rule; reconcile the no-argument routing rule so it leads the menu with init instead of silently jumping into it; extend the craft init-then-resume footnote to cover shape, now also a from-scratch flow. - skill/scripts/context.mjs: soften the NO_PRODUCT_MD message to defer to the step 1 rule instead of "Stop the current task"; refresh the stale file-level JSDoc that still described the old empty-stdout signal. - tests/skill-behavior/scenarios.test.mjs: add scenario 10 (scoped command + no PRODUCT.md proceeds without forcing init) and scenario 11 (shape + no PRODUCT.md still diverts into init). Scenario 1 (craft diverts) stays green and pins the build path. Source-only per repo convention; provider and plugin copies are regenerated by the maintainer's build:skills sync. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Fix missing-context routing for build intent --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Paul Bakaus <paul.bakaus@gmail.com> |
||
|
|
49ae0384b9 |
Fix live variant cycling hydration mismatch on SSR frameworks (#287) (#288)
* Fix live variant cycling hydration mismatch on SSR frameworks Drive variant visibility and range/toggle --p-* custom properties through an injected session stylesheet instead of mutating hidden/style on server-rendered variant divs. Fixes flaky nextjs-app-router expectConsoleClean failures (issue #287), same pattern as scroll-anchor (#276) and pick-cursor (#286). Co-authored-by: Cursor <cursoragent@cursor.com> * Refactor variant-state stylesheet for readability Extract named display constants (VARIANT_HIDE_DECL / VARIANT_SHOW_DECL) and small variantStateSelector / variantParamDecls helpers so the rule-building is self-documenting. Restore the scroll-lock comment to startScrollLock. No behavior change; regression guards updated to match. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix: keep variant-state stylesheet in sync on first-reveal and paramless cycle Stop refreshParamsPanel from removing the injected variant-state sheet during GENERATING first-reveal, and re-sync the sheet when cycling to a paramless variant so stale --p-* rules do not persist. Harden the updateVariantStateStylesheet guard to num == null || num < 1. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix: apply tuned --p-* inline for client-mounted Svelte component variants Svelte component sessions mount into [data-impeccable-component-mount] with no [data-impeccable-variant="N"] wrapper for the state stylesheet to target. Restore inline --p-* on the client-mounted element for range/toggle params while keeping the SSR div path on the injected stylesheet. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
ca121aa35f |
Fix: file-scoped wildcard ignores suppress non-value-bearing rules (#296) (#309)
A file-scoped wildcard ignore (add-value <rule> "*" --file <glob>) silently no-op'd for rules with no extractable value, such as side-tab. isIgnoredFindingValue bailed on an empty value before the wildcard/file-scope branch could run. Require a value only on the specific-value path; let the scoped wildcard match on rule + file. Mirrored in skill/scripts/hook-lib.mjs for CLI/hook parity. |
||
|
|
7b2c2a1f23 | Fix Impeccable setup path guidance (#341) | ||
|
|
95b67ffa83 |
Add configurable detector extensions for server-side templates (#347)
* Add configurable detector extensions for server-side templates (#316) Co-authored-by: Cursor <cursoragent@cursor.com> * Use imperative voice for detector.extensions guidance in hooks.md Co-authored-by: Cursor <cursoragent@cursor.com> * Route html-engine extensions through detectHtml in the Cursor pre-write gate Co-authored-by: Cursor <cursoragent@cursor.com> * Prefer the longest matching suffix in matchConfiguredExtension Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Abdul Wahab <abdulwahab@Abduls-MacBook-Pro-2.local> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
751ec31dd6 |
Fix: stop the design hook from creating .impeccable/ in unrelated projects (#346)
The PostToolUse hook was writing hook.cache.json after every edit, even when nothing was scanned or recorded. Gate the persist to earned writes only, and key the cache to the edited file's project root when the session starts from an umbrella directory. Fixes #344, #305 Co-authored-by: Abdul Wahab <abdulwahab@Abduls-MacBook-Pro-2.local> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
7501e67b55 |
Fix live toast stale callback race (#271)
Co-authored-by: Jean-Claude <273834277+jjoanna2-debug@users.noreply.github.com> |
||
|
|
b3108c1697 | Clarify bolder design-system boundaries | ||
|
|
4ac0348032 | Add Codex grid background slop rule | ||
|
|
1a3f5d78bd | Fix Codex hook manifest schema | ||
|
|
bcd16381cf | harden critique so that it runs in sub-agents more often in harnesses other than Claude and Codex | ||
|
|
da2cda06ed |
Point DESIGN.md spec links at open-source GitHub spec (#299)
* Point DESIGN.md spec links at the open-source GitHub spec. The Stitch docs site is client-rendered and unreliable for agent fetch; the google-labs-code/design.md repo tracks the latest machine-readable spec. Co-authored-by: Cursor <cursoragent@cursor.com> * Sync plugin and harness copies after DESIGN.md spec link update. build:release copies skill/reference into plugin/ and all harness dirs, so refresh those generated outputs here instead of leaving plugin/ stale. Co-authored-by: Cursor <cursoragent@cursor.com> * Use raw GitHub URL for DESIGN.md spec in agent-facing refs. The blob URL serves HTML; raw.githubusercontent.com returns plain markdown that agents can fetch directly. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
b7d2ad5589 |
Fix: allow skill's bundled node helpers under strict-permission harnesses (#301) (#310)
The skill declared only `Bash(npx impeccable *)` in allowed-tools, but Setup and the no-arg menu shell out to `node {{scripts_path}}/*.mjs`. Under a default-deny Claude Code allowlist those calls are blocked, so Setup fails on context.mjs.
Add a provider-aware `Bash(node {{scripts_path}}/*)` entry and resolve {{scripts_path}} in the frontmatter (the build previously substituted it only in the body). Provider-aware rather than the hardcoded `.claude/...` path the issue suggested, since five providers honor allowed-tools with different script dirs.
|
||
|
|
8eedb150c5 |
Fix React hydration mismatch from live pick-cursor class on SSR roots (#286)
* Fix React hydration mismatch from live pick-cursor class on SSR roots Entering pick mode toggled a `impeccable-live-pick-cursor` class on `document.documentElement` (and the insert-axis cursor wrote an inline `style.cursor` on it). `<html>`/`<body>` are server-rendered by frameworks like Next.js App Router, so a client-only attribute the server HTML never emitted makes React 19 log "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 console.error that flaked the nextjs-app-router live-e2e fixture's expectConsoleClean probe. This is the same root-cause class as the scroll-anchor lock fixed in #276 (client mutation of a hydrated SSR root), but a separate offender that fix did not cover. Apply the same shape: drive the pick / insert cursor entirely through the textContent of one injected `<style>` keyed by PICK_CURSOR_STYLE_ID, never by a class or inline style on `<html>`. Same computed effect (global `cursor` rule, reverted inside the overlay chrome), recreated on activation and removed on teardown. Regression guard updated to pin the new shape: no `document.documentElement.classList.*` mutation anywhere in the overlay, the cursor applied through the injected style, and the style removed by id on exit. Verified end-to-end: the nextjs-app-router live-e2e fixture now passes the full click -> Go -> cycle -> accept cycle with a clean console. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Remove now-dead pageInteractionCursorActive flag The flag's only reader was the old inline-style cleanup branch in syncPageInteractionCursor, which the stylesheet refactor removed. It is now write-only, so drop the declaration and both writes (Greptile review). No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
776c019041 |
Add inline, in-file ignore comments for the detector (#283) (#285)
* 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> |
||
|
|
42be79eab5 |
Fix monorepo target-selection edge cases from #213 review (#282)
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> |
||
|
|
0306b41949 |
Add monorepo context support (#213)
Context files (PRODUCT.md / DESIGN.md) resolve child-first then fall back to the repo root, and /impeccable live lets the user pick a child app in a monorepo. Single-app behavior is unchanged. Closes #202. Co-Authored-By: abdulwahabone |
||
|
|
41ff946121 |
Add GitHub Copilot hook support (CLI + cloud agent) (#279)
* 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> |
||
|
|
67e8757401 |
Fix React hydration mismatch from live scroll-lock on SSR roots (#276)
* 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> |
||
|
|
a1560fb0f5 |
Fix misleading npx hints in live-mode poll/wrap scripts (#275)
* 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>
|
||
|
|
c7539c867d | Fix live picker sizing and divider detection | ||
|
|
4f50db2bca | Fix live picker steer sizing | ||
|
|
99a284a0d9 | Fix live page editable focus handling (#256) | ||
|
|
51d01e3a5f |
[codex] Add design-aware detector rules (#252)
* Add design-aware detector rules * Fix design-aware detector noise * Unify CLI and hook detector ignores * Fix remaining design-system review findings * Add detector ignore CLI * Fix design detector review findings * Fix design color source false positives * Fix core test suite registration * Add design-aware detector docs * Fix font priority design-system parsing * Fix color ignore value matching |
||
|
|
858b9bbea6 | Improve hook false-positive handling | ||
|
|
6443980117 | Improve CLI install prompts | ||
|
|
9b0b63c04f | Prepare CLI 3.0.0, skill 3.6.0, extension 1.2.0 | ||
|
|
8cf2be110d |
feat(cli): interactive hook consent + unified .impeccable/config.json (#245)
* 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> |
||
|
|
9c0012d4e1 |
feat(hooks): package design hook in plugin, install to settings.local.json (#243)
* 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>
|
||
|
|
672517f76e |
Add automatic design hook install and exceptions (#170)
* docs: add PRD for design detector hook integration Plans a PostToolUse hook for Claude Code and Codex that runs the existing design detector after every relevant file write and feeds findings back to the agent as advisory system-reminder context. No implementation in this commit; covers UX, technical design, build pipeline changes, distribution, coverage tradeoffs, and rollout. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: revise hook PRD with best-practices review Folds in the P0/P1/P2 findings from an online best-practices critique against the official Claude Code and Codex hook references plus 10+ 2026 community guides and similar prior-art tools (claw-hooks, claude-code-hooks-mastery). Key changes: - Exec form everywhere (Codex snippet was shell form), with Windows rationale. - Default timeout dropped from 10s to 5s. - Re-entrancy guard (CLAUDE_HOOK_DEPTH) and per-file edit counter. - Session-scoped finding dedup promoted from open question to v1. - Per-language inline-ignore syntax map (HTML/JSX/CSS/JS). - Hard-skip rules for sensitive paths and generated/lock files. - Honest framing about Claude Code lacking per-plugin hook disable. - Honest framing about Bash-written files being invisible in v1. - Codex Windows-not-supported call-out, feature flag note, trust ceremony detail. - Optional NDJSON audit log via IMPECCABLE_HOOK_LOG. - Findings cap lowered 8 → 5 with attention-budget rationale. - Versioned envelope ([impeccable@1]) on rendered template. - Expanded test plan, decision log, and stdin payload appendix. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(hooks): ship the design detector hook for Claude Code and Codex Implements docs/hooks-prd.md: a PostToolUse hook that runs the impeccable design detector after every Edit/Write/MultiEdit on a UI file and pushes findings into the agent's next-turn context as a short system reminder. Silent on clean files. Never blocks an edit. Why this matters: today, design slop (side-tab borders, gradient text, purple/cyan palettes, bounce easing, etc.) only gets caught when a human notices or someone explicitly runs /impeccable audit. The hook closes the loop at the moment slop is written. What ships in v1 - skill/scripts/hook.mjs: PostToolUse entry. Reads stdin, runs the detector in-process (no `npx impeccable` cold start), emits hookSpecificOutput.additionalContext when fresh findings exist. - skill/scripts/hook-lib.mjs: extracted helpers (config, cache, filter, render, audit log, runHook orchestrator). 100% unit-testable. - skill/scripts/hook-session-start.mjs: SessionStart greeting, gated by a project-scannable probe + 30-day throttle. - skill/scripts/hook-admin.mjs: backs /impeccable hooks on/off/status/ignore-rule/ignore-file/reset. Hardening built in - Re-entrancy guard (IMPECCABLE_HOOK_DEPTH) so the hook can never recursively spawn itself. - Hard-skip regexes for sensitive paths (.env, .pem, id_rsa, secrets, credentials, .git) and generated/lock/build output. These fire before the file is even read; cannot be turned off via config. - Path-traversal check on the inbound file_path. - Session-scoped dedup keyed by (session, file, rule, line) so the same finding never lands in context twice. Prevents the ~12.5K wasted tokens per chatty session called out in the PRD. - Per-(session, file) edit counter with a one-shot suppression notice on the 7th edit, silent after. - Fail-open contract: every error path returns exit 0 with no stdout. Optional NDJSON audit log via IMPECCABLE_HOOK_LOG. Three kill switches (precedence high to low): 1. IMPECCABLE_HOOK_DISABLED env var (1/true/yes/on, case-insensitive) 2. .impeccable/hook.json `enabled: false` 3. /impeccable hooks off slash command (writes the JSON) Inline ignores are language-aware. `// impeccable: ignore <rule>` for JS/TS, `<!-- impeccable: ignore <rule> -->` for HTML/Vue/Svelte/Astro, `{/* impeccable: ignore <rule> */}` for JSX/TSX, `/* impeccable: ignore <rule> */` for CSS. `*` matches any rule. Directive applies to the next non-blank line. Same shape as ESLint, Stylelint, Biome. Build pipeline - scripts/lib/transformers/hooks.js: per-provider hooks.json builders, plus the slim .codex-plugin/plugin.json manifest. - providers.js: emitHooks: 'claude' for claude-code, emitHooks: 'codex' for codex and agents. Codex also emits emitCodexPlugin. - factory.js: emits hooks/hooks.json next to the skills tree. - build.js: syncs hooks/ into harness roots and into the slim plugin/ subtree; writes .codex-plugin/plugin.json. Build is idempotent (verified: 98 staged files unchanged across two runs). Claude Code wiring uses exec form (command + args) and the ${CLAUDE_PLUGIN_ROOT} placeholder. Matcher: Edit|Write|MultiEdit. `if:` glob filters to UI extensions before spawning Node. PostToolUse timeout 5s, SessionStart timeout 3s. Codex wiring uses ${PLUGIN_ROOT} (Codex's native placeholder), matcher Edit|Write|apply_patch, no `if:` analog (the script does the extension filter). macOS and Linux only; hooks are disabled on Windows in current Codex builds. The trust ceremony and feature flag are documented in README.md. Routing - /impeccable hooks lives outside the 23-command router table on purpose: it is plumbing, not a design skill. The hidden routing slot is added to SKILL.md alongside pin/unpin so the LLM knows to dispatch it. The 23-command count and all stale-count validators remain happy. Tests - tests/hook.test.mjs: 38 unit tests covering env parsing, config load + defaults + malformed, cache round-trip + GC, ignoreRules/minSeverity/inline ignores (all four languages), globbing with **/*/{a,b}, render template with cap + clamp + 0-line prefix drop, audit log NDJSON, payload event-name parameterization, re-entrancy, kill switches, sensitive-path + generated-path + traversal skips, allowlist filter, config ignoreFiles, edit counter cycle including the 7th-edit notice, MultiEdit and apply_patch payload shapes, detector throw swallow, malformed stdin, missing file race. - tests/hook-build.test.mjs: 18 integration tests covering hook manifest shape (matcher, timeouts, exec form, if: glob, placeholders), Codex differences (${PLUGIN_ROOT}, no if:, no SessionStart), Codex plugin manifest (no inline hooks field to avoid the duplicate-file error), routing across the hooksJsonFor table, and presence of all three committed artifacts plus the bundled detector the runtime relative-import path depends on. Full suite: 175 bun tests + 186 node tests, all green. Docs - README.md: new "Design hook" section explaining default behavior, per-project / global / inline disable paths, the JSON schema knobs, the audit log debug flag, and the slop / a11y coverage split. - HARNESSES.md: flips the `hooks` row for Codex from No -> Yes (Claude was already Yes), adds a per-harness hook-surface table with the manifest location and matcher each provider uses. Open questions from the PRD intentionally deferred to v2: Bash-write blind spot, effort-aware suppression, Stop-hook session summary, per-rule severity, async hook mode. None block v1. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix Codex hook scanning: apply_patch paths and co-located stylesheets Parse file targets from Codex apply_patch command bodies, co-scan imported and sibling CSS when UI components are edited, drop the git-sweep PostToolUse group, and align Codex SessionStart manifest and trust docs with the official hooks spec. Co-authored-by: Cursor <cursoragent@cursor.com> * Gitignore hook session cache and drop local test HTML Hook dedup/throttle state in .impeccable/hook.cache.json is per-project runtime data like other .impeccable/ sidecars. Remove an untracked bad-nested-flexbox scratch page from site/public/. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix Claude Code hook: drop Edit-only if filter so Write/MultiEdit fire Claude's if permission rule binds to one tool name, so Edit(*.{…}) never spawned the hook on Write or MultiEdit despite the matcher listing them. Extension filtering now lives in hook-lib on both Claude and Codex. Co-authored-by: Cursor <cursoragent@cursor.com> * Surface Cursor design findings via stop-hook followup Replace dropped postToolUse additional_context with afterFileEdit recording and a one-shot stop followup_message so anti-pattern nudges reach the agent. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix design hook packaging and scans * Fix Cursor hook pending bucket fallback * Fix Sass hook scan coverage * Fix Cursor hook review findings * Fix session start dead hook normalization * Fix hook config and relative scan paths * Remove SessionStart design hook * Remove redundant afterFileEdit normalization * Fix Cursor suppression and module style scans * Fix sensitive path hook filter * Fix disabled Cursor stop hook emission * Refresh hook harness artifacts * Fix Cursor hook manifest install * Add hook ignore-value support * Ignore hook runtime files locally * Fix Codex plugin hook packaging * fix: address PR review bot findings Block numeric hook depth counters from re-entering. Avoid following stylesheet imports from traversal-looking hook targets. * fix: gate ignore-value suggestions by supported rules Only render exact ignore-value commands when the same finding can be suppressed by ignoreValues. * Package Codex plugin as hook-only * Remove Codex plugin packaging * Recover hook install probe plumbing * Remove Codex hook packaging follow-up doc * Remove extra hook docs and skill wording changes * Install real design hooks via skills CLI * Add provider hook smoke runner * Fix Cursor hook delivery with preToolUse gate * Simplify Cursor hook install to preToolUse * Clarify confirmed hook exceptions * Persist hook ignores in shared config * Guard font hook exceptions * Fix hook install after main rebase * Fix hook scan target handling * fix: address hook review findings * Address hook review feedback * Stabilize DeepSeek insert live fixture * Fix Cursor hook Python shell write bypass --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
5b5e487a4f |
Improve live mode configure bar and pick UX (#242)
* 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> |
||
|
|
5fbe37c97c |
skill: remove Copy section from main design skill
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> |
||
|
|
d37be057ea |
skill: drop 4 redundant typography rules + add EMPIRICAL_VALIDATION
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> |