CLAUDE.md · diff

git:20260922.86ed7cd to git:20260923.e05bd19

34 added, 31 removed. Audit A to A.

- # CLAUDE.md — atelier project memory
+ # atelier
- Public Claude Code plugin. MIT. 7 design-automation skills sharing `.atelier/brand.json` as source of truth.
+ Public Claude Code plugin, MIT, version 1.0. Eight design-automation skills share one brand file, `.atelier/brand.json`. Users receive only `plugins/atelier/`.
- ## Build / test commands
+ ## Commands
```
- npm install
- npm test # vitest, ~54 tests (some skipped if ffmpeg missing)
- npm run lint:schemas # ajv-validate JSON schemas
- npm run demo # /atelier-demo locally
- npm run demo:gif # regenerate demos/overview.* (README embeds the animated WebP, not the GIF)
+ npm install # root dev deps; postinstall runs npm ci in plugins/atelier
+ npm run setup:browsers # Chromium matching the plugin's pinned Playwright
+ npm test # Vitest; Chromium, ffmpeg and OS-specific tests skip when unavailable
+ npm run test:coverage # 70% gate on plugins/atelier/{skills,lib}; never lower it
+ npm run lint:schemas # manifests parse, schemas compile, example brand.json validates
+ npm run check:versions # versions, README badge, CHANGELOG section agree
+ npm run smoke:plugin # marketplace-style install (npm ci --ignore-scripts, 60 s), then every skill
+ npm run demo # atelier demo into .atelier-demo/
+ UPDATE_GOLDEN=1 npx vitest run tests/runtime-ux-audit # rewrite ux goldens, then review the diff
+ node plugins/atelier/bin/atelier <doctor|demo|brand|tokens|og|images|assets|a11y|video|ux> --help
```
- CI runs Ubuntu + Windows × Node 20 with a **70% coverage gate** (thresholds in `vitest.config.mjs`, enforced by `npm run test:coverage` in `.github/workflows/ci.yml`). Don't lower it.
-
- ## Skill map
-
- `plugins/atelier/skills/<name>/` — each is self-contained and copyable in isolation:
-
- - `brand-memory` — owns `.atelier/brand.json`. **Both `loadBrand()` and `saveBrand()` must Ajv-validate** (gap fixed in v0.2.1 PR #12). Don't trust file contents.
- - `design-token-sync` — emits `tokens.css`, `tailwind.config.js`, `tokens.d.ts`, Figma vars. **All user-controlled strings emitted through `JSON.stringify`** (fix in v0.2.1 PR #12 — prevents JS literal escape).
- - `og-card-generator` — 1200×630 PNGs from HTML template.
- - `responsive-image-pipeline` — AVIF + WebP at 480/768/1280/1920 + LQIP. SHA-cached.
- - `brand-asset-pipeline` — favicons, app icons, social, Steam capsules. No `.ico` emission — see SKILL.md "Why no `.ico`" (24-bit BMP pitfall).
- - `accessibility-design-audit` — axe-core + Playwright, WCAG AA.
- - `html-to-video` — Playwright + ffmpeg → MP4/WebM. **No `shell: true` on spawn** (fix in v0.2.1 PR #11 — Windows command-injection via `outPath`). Use PATH-resolved binary lookup.
+ ## Layout
- Slash commands live alongside the skills: `/atelier-demo`, `/brand-init`, `/brand-get`, `/brand-set`, `/brand-audit`. Tests are in `tests/` (mirroring skill names), shared JSON schemas in `schemas/`, helper scripts in `scripts/` (incl. `preflight.mjs` binary-dep checks), plugin manifest in `.claude-plugin/marketplace.json`.
+ - `plugins/atelier/`: the installable unit and its own npm project (`package.json`, lockfile). Nothing outside it exists at runtime. `.claude-plugin/plugin.json` holds the version (never `marketplace.json`); `bin/atelier` is the CLI; `commands/*.md` the slash commands; `skills/<name>/{SKILL.md,index.mjs}` the 8 skills; `lib/` preflight, cli, io, doctor; `schemas/` brand and ux-audit; `examples/` and `scripts/run-demo.mjs` the demo.
+ - `tests/`: Vitest suites. Plugin deps are imported through `tests/helpers/plugin-deps.mjs`, never added to the root `package.json`.
+ - `scripts/`: repo tooling. `action.yml`: composite GitHub Action. `.github/workflows/`: `ci`, `release`, `audit`.
+ - `docs/`: `api.md` (the 1.0 semver contract), `architecture.md`, `getting-started.md`, `skill-reference.md`, `contributing.md`, `releasing.md`, `specs/`.
- ## Non-obvious conventions
+ ## Conventions
- - Exact dependency pins are intentional — `package.json` is the source of truth (Dependabot bumps them via grouped PRs, e.g. PR #4). 0.1.0 CHANGELOG documents the deviations from the original spec. **Don't loosen pins to ranges, and don't hand-edit versions — let Dependabot roll them.**
- - JSON, not YAML, for `.atelier/brand.json` — Claude edits it via slash commands; YAML's human ergonomics buy nothing.
- - Each skill verifies its binary deps at entry (sharp native, ffmpeg, Playwright browsers) and prints install instructions on miss.
- - **No telemetry. MIT.** Don't add metrics calls or analytics.
+ - Exact dependency pins, no ranges. Upgrade by hand (`npm install --prefix plugins/atelier --save-exact pkg@x.y.z`) and commit the regenerated `plugins/atelier/package-lock.json`. No Dependabot; `audit.yml` reports weekly. Nothing may rely on install scripts.
+ - brand.json is JSON, validated against `schemas/brand.schema.json` on every load and save. Only brand-memory writes it; other skills call its `loadBrand()`. In 1.x the schema only gains optional fields.
+ - No telemetry and no network calls beyond the page being audited or recorded.
+ - Never spawn through a shell: resolve with `findOnPath` or `ensureFfmpeg`, spawn with `shell: false`. Launch Chromium with `launchChromium` from `lib/preflight.mjs`.
+ - Escape brand and page values for where they land: HTML, CSS, JS string literals via `JSON.stringify`, Markdown.
+ - Exit codes: 0 ok; 1 audits found critical or serious (also `brand validate` errors, `doctor` failures); 2 usage error or could not run. `runCli` returns the code and never calls `process.exit`; importing a skill (even under `node -e`) never starts its CLI.
+ - `SKILL.md` and command files use `${CLAUDE_PLUGIN_ROOT}` and `${CLAUDE_SKILL_DIR}`. Claude Code substitutes them when loading the file; they are not env vars, so code never reads them. Never reference repo paths there.
+ - Exports, flags, outputs, exit codes and `ux-raw.json` are semver-covered: update `docs/api.md` with any change to them.
+ - Prose and tool output: ASCII, no em or en dashes, no emoji.
- ## Pending work
+ ## Testing
- `CHANGELOG.md` under `## [Unreleased]` is the source of truth. `runtime-ux-audit` *spec* was added in 0.2.0 (backdrop-filter compositor rules from goneidle.com) but is not yet a shipped skill.
+ - Every behavior change gets a test: API in `tests/<skill>.test.mjs`, CLI in `tests/<skill>-cli.test.mjs`.
+ - A runtime-ux rule needs a triggering case, a near miss, a fixture trigger and a regenerated golden. `registry.test.mjs` checks the SKILL.md rule tables, headline groups and rule counts.
+ - Run `npm run smoke:plugin` after touching dependencies, file layout or runtime path resolution.
- ## Commit conventions
+ ## Commits
- Conventional Commits. PRs are how things land (see #11, #12 for the security-fix pattern).
+ Conventional Commits (`feat`, `fix`, `docs`, `test`, `chore`, `refactor`, `ci`, `build`), scope = skill name. No AI attribution, no `Co-Authored-By` or other co-author trailers, and no bot authors: every commit has a human author. User-facing changes get an entry under `## [Unreleased]` in `CHANGELOG.md`. Releases: `docs/releasing.md`.