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>
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>
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>
CLAUDE.md said the prose validator "deliberately skips skill/", which is only
half true and cost a build failure this week: validateProse skips it, but
validateSkillProse then scans skill/**/*.md and fails the build on em dashes plus
the phrases with no technical reading. Document both gates, which files each one
reads, and the line that actually matters in practice: an em dash in
skill/reference/*.md fails the build, one in a skill/scripts/*.mjs comment does
not. Each claim was checked against a real `bun run build`.
Also record that feature PRs do not bump manifest versions or add changelog
entries. It was not written down anywhere: not CLAUDE.md, not AGENTS.md, not the
PR template. CLAUDE.md's "Bump when: CLI code changes" reads as an instruction to
bump inside the PR that touches cli/, so say plainly that it names which
component a change belongs to rather than when to edit the manifest.
Put the rule in AGENTS.md too. That is the guide the agents opening PRs here
actually read, so a rule about PR hygiene living only in CLAUDE.md would not
reach them.
Prepared with AI assistance under maintainer direction.
Co-Authored-By: Claude <noreply@anthropic.com>
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>
live-browser.js builds a self-contained UI that renders over arbitrary host
pages, so its inline type scale is deliberately independent of DESIGN.md, which
documents the impeccable website's ramp. The rule fired 32 times there and is
the only rule that fires on that file.
Suppress it as a file-scoped value wildcard rather than via ignoreFiles: an
ignoreFiles glob would silence every rule for the file, and the overlay is real
user-facing chrome where a future contrast or side-tab finding should still be
heard. Scoped to this one file, so the rule keeps working everywhere else.
Written by hand because hook-admin's ignore-value cannot emit the `files` array
that detector.ignoreValues supports and existing entries already use.
Prepared with AI assistance under maintainer direction.
Co-Authored-By: Claude <noreply@anthropic.com>
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>
Follow-up on review feedback. The previous commit consolidated four of the six
Live benchmark parsers and left these two on their own hand-rolled `arg()`,
which was the inconsistency the first pass was meant to remove.
- benchmark-live-control.mjs and benchmark-live-init.mjs parsed --iterations
with Number(), so a non-numeric value became NaN and `index < NaN` ran the
benchmark zero times before failing on the metrics file. They also accepted
only the space-separated form, so --iterations=20 silently measured the
default. Both now use parseArgs + positiveIntFlag, which throws on a value
that was clearly meant as a number.
- benchmark-live-control.mjs read the metrics file with no handling for the case
where the run produced nothing: a missing file surfaced as a raw ENOENT stack
and a malformed line as a bare SyntaxError. Report both with a diagnostic
naming the file and the env var that populates it.
- summarize() now reports a `samples` count and nulls instead of letting
percentile() read past an empty array, where the NaN serialized to null and a
report of nothing measured looked like a real measurement.
Prepared with AI assistance under maintainer direction.
Co-Authored-By: Claude <noreply@anthropic.com>
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>
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.
* 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.
* 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>
Lower the CLI engine floor to Node 22.12 so npx no longer falls back to stale 2.x releases for Node 22/23 users.
Add Node 22.12 CI coverage while preserving the stable required test check, and document the 3.2.1 CLI release notes including the detector and installer fixes already waiting on main.
AI-assisted-by: Codex
* Fix docs UI polish
* Add CI retrigger spacing
* Remove CI retrigger spacing
* Fix docs demo after panel light mode
* Revert "Fix docs demo after panel light mode"
This reverts commit 3b2ffd37af.
* Scope docs demo after panel by theme
* Use lacquer black for docs demo after panel
* Use lacquer token for docs demo after panel
* 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>
* 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>
* 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>
* 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>
* 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>
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.
* Fix Pi global install path
* Simplify Pi skills-path helpers and consolidate tests
One userProviderSkillsDir helper owns the HOME_SKILLS_DIR_OVERRIDES
lookup, read paths share existingSkillsDirs, and the five Pi install
tests collapse into two that keep the same coverage: global detection
plus the agent-path write, and project scope in a home-rooted repo.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Respect requested scope when resolving Pi skills dirs
An explicit install scope now narrows providerSkillsDirCandidates to
the matching layout, so a project-scope install in a home-rooted repo
no longer matches an existing global Pi install and get swallowed by
the already-installed refresh path. Update/check flows still probe
both layouts since they have no scope. Covers the T-Rex repro in the
home-rooted regression test.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Refresh every existing Pi layout on unscoped update
deduplicateProviders keeps one entry per existing layout instead of
only the first, so unscoped check/update refresh both ~/.pi/agent/skills
and ~/.pi/skills when a home-rooted repo holds copies in each. Home-dir
detection now compares realpaths, since findProjectRoot resolves
symlinks while homedir() does not.
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>
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>