diff --git a/.atl/AGENTS.md b/.atl/AGENTS.md index d6545e9f..071a54aa 100644 --- a/.atl/AGENTS.md +++ b/.atl/AGENTS.md @@ -18,10 +18,12 @@ Keep it minimal — detailed workflows live in skills (`.atl/skills/`). | Contributing | [`docs/CONTRIBUTING.md`](../docs/CONTRIBUTING.md) | | Visual Design | [`docs/DESIGN.md`](../docs/DESIGN.md) | -**Stack**: React 19 · TypeScript strict · Tailwind v4 `@theme` · Radix UI · CVA · Storybook 8 · Biome · Lefthook · Vite · pnpm +**Stack**: React 19 · TypeScript strict · Tailwind v4 `@theme` · Radix UI · CVA · Storybook 8 · Biome · Lefthook · Vite · pnpm 10.34.1 **Available tooling**: +- `pnpm run build` — builds the publishable package and sanitizes generated declarations +- `pnpm run verify:package` — builds, packs, and verifies real React 18 + React 19 consumers - `pnpm run storybook` — starts Storybook with hot reload - `pnpm run test` — runs Vitest unit tests - `pnpm run test:coverage` — runs tests with coverage report @@ -56,6 +58,9 @@ Components live in `src/components/{atoms|molecules|organisms}/{kebab-name}/` wi - Token reference: `docs/DESIGN.md` — read it before building any component - Never modify `theme.css` without explicit user confirmation - Never add dependencies without explicit user confirmation +- Repo package manager contract is `pnpm@10.34.1`; when docs, scripts, or workflows pin pnpm, use the exact version, not only `10` +- For package-facing changes (package output, exports, generated declarations, peer ranges, React major upgrades, or CI/package distribution policy), require `pnpm run build`; require `pnpm run verify:package` when published output or consumer compatibility can change. Tests/Storybook alone are not enough for React major upgrades. +- Generated declarations must not leak internal path aliases or CSS side-effect imports into the published `.d.ts` output - English only — code, comments, stories - Commit messages must follow the commitlint-enforced Conventional Commit format: `(): `. PR titles should follow the same format for review consistency. Allowed types: `build`, `chore`, `ci`, `docs`, `feat`, `fix`, `perf`, `refactor`, `revert`, `style`, `test`. Use scopes for domains such as `a11y`, `tokens`, or `infra` instead of inventing custom types. @@ -72,9 +77,9 @@ Load the relevant skill for detailed workflows: | Reviewing an existing component — code quality, visual states, tokens, accessibility | [`components-auditor`](skills/components-auditor/SKILL.md) | | Auditing the design system package — token architecture, folder structure, npm distributable standards | [`system-auditor`](skills/system-auditor/SKILL.md) | | External project wants to use Stack-and-Flow as a base and customize its tokens | [`bootstrapping`](skills/bootstrapping/SKILL.md) | -| Contributor asks if their PR is ready for review, or agent must validate implementation before handoff | [`pr-reviewer`](skills/pr-reviewer/SKILL.md) | -| Creating or auditing GitHub Project board tasks — issues, fields, phases, team assignments | [`github-project-tasks`](skills/github-project-tasks/SKILL.md) | -| Creating a release, bumping versions, generating changelogs, publishing to npm | [`release-changeset`](skills/release-changeset/SKILL.md) | -| Preparing the package for npm distribution — exports map, vite lib mode, package.json hygiene, TypeScript declarations | [`npm-architect`](skills/npm-architect/SKILL.md) | +| Contributor asks if their PR is ready for review, or agent must validate implementation before handoff, including package evidence when package-facing changes exist | [`pr-reviewer`](skills/pr-reviewer/SKILL.md) | +| Creating or auditing GitHub Project board tasks — issues, fields, phases, team assignments | [`github-project-tasks`](skills/github-project-tasks/SKILL.md) | +| Creating a release, bumping versions, generating changelogs, publishing to npm | [`release-changeset`](skills/release-changeset/SKILL.md) | +| Preparing or auditing package distribution policy — exports map, package.json hygiene, package verification, consumer compatibility, TypeScript declarations | [`npm-architect`](skills/npm-architect/SKILL.md) | | Visual review — component states, glow, transitions, focus rings, contrast, accessibility visuals | [`visual-review`](skills/visual-review/SKILL.md) | | Creating git worktrees or isolated writer checkouts for this repo | [`worktree-location`](skills/worktree-location/SKILL.md) | diff --git a/.atl/skills/npm-architect/SKILL.md b/.atl/skills/npm-architect/SKILL.md index d96d4dc8..7ecaab3f 100644 --- a/.atl/skills/npm-architect/SKILL.md +++ b/.atl/skills/npm-architect/SKILL.md @@ -16,7 +16,8 @@ metadata: - Someone asks how to publish the design system as an npm package - Someone asks how to enable per-component imports (`import { Button } from '@stack-and-flow/design-system/button'`) -- Someone asks to configure tree-shaking, peer deps, or the build output +- Someone asks to configure tree-shaking, peer deps, the build output, or package verification policy +- Someone asks to review package exports, generated declarations, React peer compatibility, or CI/package manager contract drift - Delegated from `sdd-design` to design the distribution architecture --- @@ -45,7 +46,18 @@ When delegated: run requested scope, return SDD return envelope: Do not trust frozen package-state tables in this skill. The npm package shape changes over time, so the current state must be verified at runtime before proposing exports, build, dependency, or publishing changes. -Use Phase 1 to inspect `package.json`, `src/index.ts`, `vite.config.ts`, `tsconfig.build.json`, and the existing component barrels. Report what exists now versus what the requested package goal requires. +Use Phase 1 to inspect `package.json`, the package scripts, the publish/consumer verification scripts, workflow pnpm pinning, and the existing exports/build setup. Report what exists now versus what the requested package goal requires. + +## Current repo maintenance contract + +Treat these as the current Stack-and-Flow package gates unless the repo changes again: + +- `package.json` is the source of truth for the package manager contract: `pnpm@10.34.1` +- GitHub workflows that install pnpm should pin the same exact version, not a floating major such as `10` +- `pnpm run build` is the publish-shape gate: it builds `dist/`, emits declarations, and runs `scripts/prepare-dist.mjs` +- `pnpm run verify:package` is the consumer-compatibility gate: it builds, packs, and verifies real React 18 and React 19 consumers +- Published declarations must not leak internal alias specifiers (`@/`, `@atoms/`, `@molecules/`, `@organisms/`, `@hooks/`, `@utils/`) or CSS side-effect imports +- Apply these checks only when the work changes package output, exports, generated declarations, peer ranges, React major versions, or CI/package distribution behavior. Do not force routine component-only PRs through a package architecture audit. --- @@ -54,17 +66,29 @@ Use Phase 1 to inspect `package.json`, `src/index.ts`, `vite.config.ts`, `tsconf Before making any change, read and report the current state: ```powershell -# Check package.json fields -Get-Content package.json | ConvertFrom-Json | Select-Object name, version, private, exports, files, main, module, sideEffects +# Package contract +Get-Content package.json | ConvertFrom-Json | Select-Object packageManager, exports, files, main, module, types, sideEffects, peerDependencies -# Check if root entry exists -Test-Path src/index.ts +# Build and consumer verification commands +Get-Content package.json | ConvertFrom-Json | Select-Object -ExpandProperty scripts | Select-Object build, 'verify:package' -# Check vite config for lib mode -Select-String "lib" vite.config.ts +# Workflow pnpm pinning +Get-ChildItem .github/workflows/*.yml | Select-String 'pnpm/action-setup|version:' + +# Published output hygiene / consumer verification helpers +Get-Content scripts/prepare-dist.mjs +Get-Content scripts/verify-package-consumption.mjs ``` -Report what exists vs what is needed. Present the full gap table before proceeding. +At minimum, report: + +- exact `packageManager` value +- whether workflows pin the same pnpm version +- what `pnpm run build` does to publishable output +- whether `verify:package` covers React 18 and React 19 consumers +- whether generated declarations are sanitized for published consumption + +Present the full gap table before proceeding. --- @@ -199,7 +223,7 @@ This requires an `exports` map in `package.json` with one entry per component. "types": "./dist/atoms/text/index.d.ts" }, "./styles": { - "import": "./dist/styles/theme.css" + "import": "./dist/design-system.css" } } } @@ -218,91 +242,24 @@ import '@stack-and-flow/design-system/styles'; ## Phase 4 — Vite lib mode configuration -The current `vite.config.ts` is configured for **development** (app mode). It needs a separate **build** configuration for **library mode**. - -### Strategy: separate config files +Inspect the current Vite build before proposing a package architecture change. In the current repo, `vite.config.ts` already contains the library build configuration used by `pnpm run build`; do not assume a separate `vite.config.lib.ts` exists or is required. -Do NOT modify the existing `vite.config.ts` — it is needed for Storybook and dev. Create a new file: +Current library-build expectations: -``` -vite.config.lib.ts ← library build only -vite.config.ts ← dev + Storybook (unchanged) -``` +- `build.lib.entry` points at `src/index.ts` +- ESM and CJS outputs are emitted from the configured Vite build +- React, React DOM, JSX runtime, and icon package imports stay externalized for consumers +- CSS output resolves to the exported package styles entry, currently `./styles` -> `./dist/design-system.css` +- `tsconfig.build.json` and `scripts/prepare-dist.mjs` complete the declaration-output contract after Vite runs -### vite.config.lib.ts +Only introduce a separate Vite library config if the current config can no longer serve Storybook/test/dev and package output safely. If you propose that split, explain why the current unified config is insufficient and update scripts, exports, and verification evidence together. -```typescript -import path from 'path'; -import tailwindcss from '@tailwindcss/vite'; -import react from '@vitejs/plugin-react-swc'; -import { defineConfig } from 'vite'; -import { glob } from 'glob'; - -// Collect all component entry points automatically -const componentEntries = Object.fromEntries( - glob - .sync('src/components/{atoms,molecules}/{*}/index.ts') - .map((file) => { - const name = file - .replace('src/components/atoms/', 'atoms/') - .replace('src/components/molecules/', 'molecules/') - .replace('/index.ts', ''); - return [name, path.resolve(__dirname, file)]; - }) -); - -export default defineConfig({ - plugins: [react(), tailwindcss()], - resolve: { - alias: { - '@': path.resolve(__dirname, './src') - } - }, - build: { - lib: { - entry: { - index: path.resolve(__dirname, 'src/index.ts'), - ...componentEntries - }, - formats: ['es', 'cjs'] - }, - rollupOptions: { - // Externalize everything that consumers must install themselves - external: [ - 'react', - 'react-dom', - 'react/jsx-runtime', - /^@radix-ui\/.*/, - /^lucide-react.*/, - 'class-variance-authority', - 'clsx', - 'tailwind-merge', - 'spinners-react' - ], - output: { - // Preserve directory structure in output - preserveModules: true, - preserveModulesRoot: 'src', - // Separate CSS from JS - assetFileNames: 'styles/[name][extname]', - globals: { - react: 'React', - 'react-dom': 'ReactDOM' - } - } - }, - // Do not minify library code — consumers' bundlers will do it - minify: false, - // Generate sourcemaps for debugging - sourcemap: true - } -}); -``` +### Keep the publish build command explicit -### Add build script to package.json +If the repo already ships a publish build, keep that command as the source of truth and make sure it includes declaration sanitization after emit. In the current repo, that contract is: ```json -"build:lib": "vite build --config vite.config.lib.ts && tsc --emitDeclarationOnly --declaration --outDir dist" +"build": "vite build && tsc --project tsconfig.build.json && node scripts/prepare-dist.mjs" ``` --- @@ -367,7 +324,7 @@ Tells bundlers which files have side effects (cannot be tree-shaken away). ```json { "sideEffects": [ - "dist/styles/theme.css", + "dist/design-system.css", "**/*.css" ] } @@ -391,75 +348,39 @@ For compatibility with older tooling that does not read `exports`: ## Phase 6 — TypeScript declarations -The `tsc` command in the build script generates `.d.ts` files. Ensure `tsconfig.json` has: +The declaration gate is the published `.d.ts` output, not only the TypeScript compiler flags. -```json -{ - "compilerOptions": { - "declaration": true, - "declarationDir": "dist", - "emitDeclarationOnly": false - } -} -``` +`pnpm run build` already emits declarations and then runs `scripts/prepare-dist.mjs`, so inspect both the tsconfig settings and the post-processing script before proposing declaration changes. -Or use a separate `tsconfig.build.json` that extends the base and overrides emit settings. - ---- +Declaration checklist: -## Phase 7 — Verify the build +- emitted `.d.ts` files resolve through public or relative specifiers, never internal aliases such as `@/` or `@atoms/` +- `dist/index.d.ts` does not keep CSS side-effect imports such as `import './styles/global.css';` +- post-build sanitization stays aligned with the actual alias scheme used in source +- declaration fixes should target publish-safe output; do not require committing generated `dist/` files unless the workflow explicitly does so -After configuring everything: - -```powershell -# Build the library -pnpm build:lib - -# Check the output structure -Get-ChildItem dist -Recurse | Select-Object FullName -``` - -Expected output structure: +--- -``` -dist/ - index.js ← ESM full bundle - index.cjs ← CJS full bundle - index.d.ts ← TypeScript declarations - atoms/ - button/ - index.js - index.cjs - index.d.ts - input/ - index.js - ... - styles/ - theme.css ← design tokens -``` +## Phase 7 — Verify package output -### Smoke test the package locally +Run this phase when the work changes package output, exports, generated declarations, peer ranges, CI/package distribution policy, or a React major version. Skip it for routine component-only changes that do not affect published package behavior. ```powershell -# Pack without publishing -pnpm pack +# Publish-shape gate +pnpm run build -# In a test project -pnpm add ../stack-and-flow-design-system-1.0.0.tgz +# Consumer-compatibility gate +pnpm run verify:package ``` -Then in the test project: +Expected evidence: -```typescript -// Full package import -import { Button } from '@stack-and-flow/design-system'; - -// Per-component import -import { Button } from '@stack-and-flow/design-system/button'; +- `pnpm run build` succeeds and leaves publishable output in `dist/` +- `scripts/prepare-dist.mjs` has sanitized generated declarations for publish-safe consumption +- `pnpm run verify:package` proves the packed package works for real React 18 and React 19 consumers +- ESM/CJS root imports and the exported styles subpath resolve successfully -// Styles -import '@stack-and-flow/design-system/styles'; -``` +If the request is only documentation or skill text, do not run these commands just to satisfy ceremony. Cite the contract accurately instead. --- @@ -469,22 +390,17 @@ import '@stack-and-flow/design-system/styles'; |----------|--------|----------|--------| | Build tool | Vite lib mode | Rollup standalone | Already in project; consistent with dev config | | Module format | ESM + CJS | ESM only | CJS needed for Jest/older Node consumers | -| Separate config file | `vite.config.lib.ts` | Modify existing config | Existing config is used by Storybook and dev — cannot change mode | +| Vite config shape | Current `vite.config.ts` unless evidence requires split | Assume `vite.config.lib.ts` is needed | The current repo already builds the library from `vite.config.ts`; propose a split only with concrete evidence | | CSS distribution | Separate `./styles` export | Inline-in-JS | Inline CSS-in-JS would require a runtime; separate file is simpler and standard | -| `preserveModules` | true | Single bundle | Enables per-component tree-shaking at the file level | -| Radix in `dependencies` | Yes (not peerDep) | peerDep | Consumers may not have Radix; forcing them to install it is poor DX | +| Package verification | `pnpm run verify:package` with React 18 + React 19 consumers | Tests/Storybook only for package compatibility | Consumer installs catch peer/export/declaration failures that app-level tests can miss | +| Declaration hygiene | Post-build sanitized `.d.ts` output | Leaking source aliases or CSS side-effect imports | Published declarations must resolve outside this repository | --- ## Implementation order -Follow this sequence — each step depends on the previous: +For new package architecture work, follow the phases above in dependency order: first audit current package state, then align entry points, exports, Vite build behavior, package metadata, declarations, and verification evidence. -1. **Phase 2** — create `src/index.ts` (no build changes yet, safe to do first) -2. **Phase 5** — fix `package.json` (peer deps, files, sideEffects — no build needed) -3. **Phase 4** — create `vite.config.lib.ts` (requires Phase 2 entry point to exist) -4. **Phase 3** — add `exports` map to `package.json` (requires knowing dist paths from Phase 4) -5. **Phase 6** — verify TypeScript declaration config -6. **Phase 7** — run the build and verify output +For current-maintenance work, do not blindly recreate old setup steps. Start from the existing `package.json`, `vite.config.ts`, `tsconfig.build.json`, `scripts/prepare-dist.mjs`, `scripts/verify-package-consumption.mjs`, and CI workflow pins, then change only the piece that the request requires. -Do NOT skip steps or reorder — particularly, Phase 3 (exports map) references `dist/` paths that only exist after Phase 4 is configured and built. +Do NOT skip verification for package-facing changes: exports, build output, generated declarations, peer ranges, React major versions, and CI/package distribution policy require build and package-consumption evidence. diff --git a/.atl/skills/pr-reviewer/SKILL.md b/.atl/skills/pr-reviewer/SKILL.md index c71d2cc4..ba01d934 100644 --- a/.atl/skills/pr-reviewer/SKILL.md +++ b/.atl/skills/pr-reviewer/SKILL.md @@ -13,6 +13,8 @@ metadata: Use this skill immediately before requesting human review or opening/submitting a PR. This is a **PR gate**, not the owner of component implementation rules. +Package verification checks in this skill are conditional. Apply them only when the diff touches package output, exports, generated declarations, peer ranges, React major versions, or CI/package distribution behavior. Do not turn routine component PRs into package architecture reviews. + For component-quality details, load: - `.atl/skills/_shared/component-contract.md` @@ -31,6 +33,7 @@ Run first. If any check fails, return **REJECTED** and do not continue to option | Check | How to verify | | --- | --- | | CI/build/test evidence missing or failing | Inspect CI output or run the agreed local commands. | +| Package compatibility evidence missing for package-facing changes | When the diff changes package output, exports, generated declarations, peer ranges, CI/package distribution policy, or a React major version, require `pnpm run build` and `pnpm run verify:package` evidence. Tests/Storybook alone are insufficient for React major upgrades. | | No linked issue | PR description must contain `Closes #NNN` unless maintainer explicitly waives it. | | Invalid PR title | Conventional Commit format: `(): `. | | PR template incomplete | No required placeholder sections left empty. | @@ -70,6 +73,8 @@ Do not duplicate the component checklist here; cite the audit evidence and only - [ ] TypeScript check passed or failure is explained. - [ ] Relevant unit tests passed. - [ ] Build passed when package/config/source exports changed. +- [ ] `pnpm run verify:package` evidence is included when package output, exports, generated declarations, peer compatibility, CI package policy, or React major versions changed. +- [ ] Generated declarations are publish-safe when declaration/package output changed: no leaked internal aliases or CSS side-effect imports. - [ ] Storybook build or manual visual evidence is included when stories/visuals changed. - [ ] Accessibility evidence is included for interactive components. diff --git a/.atl/skills/release-changeset/SKILL.md b/.atl/skills/release-changeset/SKILL.md index edabdc09..8c99bffc 100644 --- a/.atl/skills/release-changeset/SKILL.md +++ b/.atl/skills/release-changeset/SKILL.md @@ -134,6 +134,7 @@ A commit NEEDS a changeset if it: A commit does NOT need a changeset if it: - Only changes docs (`docs:`) +- Only changes agent/docs policy text such as `.atl/AGENTS.md` or `.atl/skills/*` with no consumer-visible package/runtime behavior change (`docs:` or `chore:`) - Only changes tests (`test:`) - Only changes CI, internal tooling, or config (`ci:`, `chore:`, `build:`) - Only changes formatting (`style:`)