Astryx is a public design-system repository. Never commit internal links, identifiers, service names, private operational instructions, or other Meta-only context.
This AGENTS.md is the canonical, tool-agnostic instruction surface for the
repository. Add or change shared agent guidance here rather than duplicating it
in tool-specific instruction files. Put genuinely path-specific guidance in a
nested AGENTS.md.
- Product builders: use
astryx docs, component{Name}.doc.mjsfiles, andpackages/cli/assets/docs/. - Contributors: read
CONTRIBUTING.mdand the relevant guidance linked fromdocs/README.md. - Component work: read the component's
{Name}.spec.mdwhen one exists, then anymodule:*records it lists for the public module being changed, followed by consumer docs, tests, and implementation. - Cross-component work: read the relevant contract under
docs/families/, applicable design spec underdocs/design/, and current architecture underdocs/architecture/. - Consequential shared-system changes: use a record under
docs/specs/.
Knowledge records declare authority:
draft: not authoritative; may still need evidence or owner review;current: explicitly approved and authoritative;archived: context only, with a reason such assuperseded,withdrawn, orhistoricaland a replacement link when one exists.
Only current records govern implementation and review. Never infer approval
from merged code, silence, an old review, or an existing wiki page.
Resolve checkable behavior from code, tests, and browser evidence. Ask a human only when a stable public API, theme contract, ownership boundary, compatibility policy, or genuinely subjective visual direction remains undecided. Ask one question at a time.
Before drafting, reviewing, or implementing a proposed outcome, search current records and open pull requests using the proposed canonical owner/id, affected paths and exported symbols, and the behavior's semantic terms. Extend or project the existing canonical owner by default. Create a new record only for a distinct fact boundary, and state why the existing owner cannot contain it. Do not create new policy for work that is already complete, owned, or superseded.
Run pnpm check:knowledge after editing knowledge records or templates. A
material template-shape change requires a schema-version bump and migration of
active records; changing template guidance alone does not rewrite accepted
history.
Tests how well AGENTS.md helps LLMs generate correct Astryx component code.
Usage:
/vibe-test 5 # Run 5 stratified sample tests (one-shot)
/vibe-test # Run all 21 tests (one-shot)
/vibe-test 5 --degradation # Run 5 tests with degradation curve (10-turn)
How to execute:
- Run
pnpm -F @astryxdesign/vibe-tests interactive --sample <count>to set up iteration - Spawn parallel subagents (one per test prompt) to:
- Read the task file from
results/<iteration>/tasks/{promptId}.json - Generate code for the prompt using Astryx components (AGENTS.md auto-injected)
- Self-evaluate for success/escape hatches
- Write
.tsxresult toresults/<iteration>/results/{promptId}.tsx - Write
.jsonmetadata toresults/<iteration>/results/{promptId}.json
- Read the task file from
- Trigger
gh workflow run vibe-screenshots.ymlto build previews and capture screenshots - Run
pnpm -F @astryxdesign/vibe-tests aggregate --iteration <id>to see results
Degradation mode (--degradation): Tests context retention across 10-turn conversations with filler, distractor, and recovery turns. Probes at turns 0, 6, 8, 10 to measure quality degradation. Results show a line graph of each test's progression.
Result format:
{
"id": "<iter>-<promptId>",
"timestamp": "...",
"model": "claude-code-interactive",
"persona": "naive",
"promptCategory": "...",
"trajectoryDepth": 0,
"prompt": "...",
"response": "<code>",
"evaluation": {"success": true, "componentsUsed": [...], "escapeHatches": [...]}
}Runners may also write an optional <promptId>.provenance.json sidecar beside the result metadata. The versioned, executor-neutral contract and fallback behavior are documented in internal/vibe-tests/docs/execution-provenance.md.
For architectural context, decisions, and research, see the GitHub Wiki:
- Decisions — API Conventions, Why StyleX, StyleX Distribution
- Architecture — System Architecture, Component Authoring Guide
- Research — AI + Design Systems, AI Model Trajectory, Swizzle Ergonomics
- Future — Animation System, RSC Utilities, Distribution Strategy
For component-specific documentation, see the {Name}.doc.mjs file in each component directory under packages/core/src/ (e.g. Button/Button.doc.mjs). These are plain JS files with JSDoc type annotations exporting a ComponentDoc object (typed via @astryxdesign/cli/authoring).
Documentation lives in two places:
- File Headers — Each source file has a structured JSDoc header with
@input,@output,@position - Component Docs —
{Name}.doc.mjsfiles in each component directory (props, features, examples)
Update Protocol: When modifying code, update the file's header comment. Look for SYNC: comments as reminders.
Audience: every .doc.mjs, and everything under packages/cli/assets/docs/, is written for people building with Astryx — not for people building Astryx. Rubrics, readiness gates, audit checklists and lab→core criteria belong in the wiki. packages/cli/assets/docs/README.md has the test and the page each kind of material goes to.
- Package manager: pnpm 11, pinned by the
packageManagerfield (see CONTRIBUTING.md for install options — Corepack is one of several, and Node 25+ no longer bundles it) - Testing: Vitest (colocated tests)
- Components:
packages/core/ - Storybook:
apps/storybook/
@examplecode fences must use plain```, not```tsx. Storybook's autodocs parser doesn't handle language-tagged fences in JSDoc correctly — the code block won't render as a proper code block. Always use untagged fences in@exampleblocks.
[StyleX v0.17.5 CSS Support]|Use CSS-native solutions. Don't build JS workarounds for supported features. |AT-RULES: @media, @supports, @container (+named), @starting-style, @scope — YES |AT-RULES: @layer, @property (explicit) — NO (compiles but invalid CSS output) |PSEUDO-CLS: :hover, :focus, :focus-visible, :focus-within, :active, :disabled — YES |PSEUDO-CLS: :first-child, :last-child, :nth-child(), :where(), :is(), :has(), :not() — YES |PSEUDO-CLS: :placeholder-shown, :checked, :empty, :modal, :user-valid, :user-invalid — YES |PSEUDO-EL: ::before, ::after, ::placeholder, ::selection, ::backdrop, ::marker, ::view-transition-_ — YES |COMPOUND: ::backdrop+condition, RTL :is([dir="rtl"] _), nested @media+pseudo — YES |VALUES: var(), calc(), clamp(), light-dark(), color-mix(), container-type/name — YES |ANIM: transition (shorthand+individual), transitionBehavior:allow-discrete, animation, stylex.keyframes — YES |WHEN: stylex.when.ancestor(':hover'/':focus-within'/':active'/':disabled') — YES |WHEN: stylex.when.descendant(':hover'), siblingBefore(':checked'), siblingAfter(':checked'), anySibling(':hover') — YES |WHEN: stylex.when.ancestor('[data-attr]') — NO (pseudo selectors only, must start with ":") |NESTING: CSS nesting with & — NO (use stylex.when.ancestor/descendant/sibling for parent-child state) |API: stylex.firstThatWorks() for CSS fallbacks (e.g. display: grid with flex fallback) — YES |API: stylex.positionTry() for anchor positioning @position-try — YES |API: stylex.types.color/length/etc for typed CSS variables in defineVars — YES |API: stylex.defineConsts() for compile-time constants — YES |DYNAMIC: Functions in stylex.create for runtime values — YES |VARS: stylex.defineVars, stylex.createTheme (require .stylex.ts files) — YES |LAYOUT: grid, flex+gap, aspect-ratio, overscrollBehavior, scrollbar-gutter/width — YES |PATTERN: dialog entry animation -> @starting-style (not useState+rAF) |PATTERN: parent hover child style -> stylex.when.ancestor(':hover', marker) (not CSS nesting). Use stylex.defineMarker() in a .stylex.ts file for scoped markers. Ancestor element MUST have marker.marker in its stylex.props() call. NEVER use stylex.defaultMarker() for form controls (CheckboxInput, RadioList, Switch) — it leaks hover/focus-within from outer containers like Popovers. Always use a component-scoped defineMarker() instead. |PATTERN: hover on touch -> @media (hover: hover) guard |PATTERN: zebra striping -> :nth-child(even) (not index%2 JS) |PATTERN: container responsive -> @container (not ResizeObserver) |PATTERN: CSS fallback values -> stylex.firstThatWorks() (not manual fallback) |PATTERN: dynamic/runtime values -> stylex.create({ s: (val) => ({ prop: val }) }) (not inline styles) |PATTERN: conditional styles -> stylex.props(condition && styles.x) (not className toggling) |PATTERN: link elements -> useLinkComponent() (not hardcoded ). Consumers swap via LinkProvider for framework routers (Next.js, React Router) |VERIFY: node internal/stylex-capabilities/scan.mjs
Astryx CLI|Run from repo root. Load agent docs before any component work. astryx() { node packages/cli/clients/cli/bin/astryx.mjs "$@"; } BOOTSTRAP (run every branch, <500ms): astryx help # discover all commands and options astryx docs # list available doc topics astryx docs principles --dense # design rules, anti-patterns, xstyle, tokens astryx docs tokens --dense # spacing, color, radius, typography, shadow astryx docs theme --dense # theme provider, light/dark, overrides astryx component --list # all components grouped by category astryx template --list # available page templates ON DEMAND: astryx component --dense # props, variants, usage, anatomy for one component astryx template # emit full page source astryx template --skeleton # layout skeleton with spatial annotations astryx swizzle # eject component source for deep customization astryx upgrade --apply # run version migration codemods OPTIONS: --detail compact|brief less output | --dense token-efficient | --zh Chinese RULE: always run bootstrap on each branch — docs reflect the branch's actual API RULE: always run astryx component --dense before modifying a component RULE: after @astryxdesign/core bump, always run astryx upgrade --apply