Skip to content

Contributing

Install Node 24+ and Bun 1.4.0, then run bun install --frozen-lockfile --ignore-scripts. For one package use bun run turbo run build typecheck --filter=@alneas/appkit-core (or appkit-react). Do not rerun unrelated integration tests for unchanged implementations.

  1. Change the owning source and API JSDoc.
  2. Update its English feature guide, including the exact public import subpath. Install commands use package names, not subpaths.
  3. Run affected tests and packaging/type checks. Record commands and identify reused evidence in the PR.
  4. Open a PR explaining the problem and resulting behavior. Version 0.1.0 is already published; review later version changes together with internal pins. Working-tree changes are candidates for a future release, not updates to the existing npm version.

bun run check:release checks public entry loading, root APIs and packaged files. bun run check:docs checks selected sources, subpaths and relative links. Builds generate the export reference and AI indexes; do not hand-edit generated files.

Routine CI uses Turbo caching for builds, types and unit tests. Browser/database checks are selected when their integration boundaries change. Releases reuse the exact source commit’s successful main quality run for browser evidence. Cache hits are prior passing results for matching source, helpers, dependencies and Node/Bun/platform runtime.

Keep temporary fixtures in .verification/tmp and logs in .verification/logs. Packed consumers use a disposable system-temp directory so undeclared imports cannot resolve through checkout node_modules. Both are excluded from Git and public manifests. Public and private remain independent repositories with separate lockfiles; private uses exact published public versions. Only explicitly selected disposable consumer fixtures replace registry versions with reviewed tarballs.

Ignored files are not automatically safe to share. Verification receipts, including archives.json, can contain absolute archive paths and environment details. Keep original receipts local so their hashes remain usable; inspect and redact a separate copy before sharing it. Do not upload raw .verification/ files as CI artifacts or force-add them to Git. Check logs for credentials without printing matching values; revoke an exposed credential if one is confirmed.

Before committing, check git var GIT_AUTHOR_IDENT and git var GIT_COMMITTER_IDENT. Use your GitHub-provided noreply address in repository-local Git configuration if you do not want a personal email in future public commits. Changing configuration does not remove an address from existing history; coordinate any history rewrite separately.

The portal owns translation, ingestion and deployment. Follow https://tech-docs.alneas.com/policies/documentation-policy.md and https://tech-docs.alneas.com/skills/document-authoring/SKILL.md. The root GitHub README is plain Markdown; portal landing pages carry their own frontmatter.

After bun install --frozen-lockfile --ignore-scripts, run bun run secrets:setup and bun run hooks:install once in each clone. The installer downloads the pinned Gitleaks release and verifies its SHA-256; macOS and Linux on arm64/x64 are supported. Other platforms can supply the same Gitleaks version on PATH or through APPKIT_GITLEAKS_BINARY. No provider credentials are needed.

Husky runs bun run secrets:staged and bun run lint:staged before each commit. A missing scanner or failed check blocks the commit. Gitleaks scans the staged Git patch with redacted output; lint-staged runs read-only Biome lint checks on staged JavaScript, TypeScript and JSON files. Neither command automatically formats files. Unstaged changes stay untouched. For partially staged files, lint checks the working copy; separate unfinished edits before committing.

Before a push, Husky runs bun run verify:push. This always scans history for secrets and runs source lint, then selects package, documentation, tooling, React/Core compatibility and browser checks from changed inputs. Turbo reuses matching build/type/unit results. This command does not run live-model evaluation or publish packages. Git hooks can be bypassed, so the Node 24 CI job uses the same selector and runner after installing Gitleaks. Node 26 checks affected package builds, types and unit tests. CI still runs only on main updates or manual dispatch.

Local selection compares the merge base of HEAD and origin/main with the working copy, including staged, unstaged and untracked files. Fetch main before starting work. CI compares its exact commit with the latest successful main-push Public quality run that is an ancestor. Failed, cancelled and superseded runs do not advance that baseline: the next run includes all outstanding changes. If history or API evidence cannot establish a baseline, run the full gate. Manual dispatch also runs the full gate; it cannot substitute for main-push release evidence.

Changed inputs Selected checks
Prose or source comments/JSDoc with unchanged parsed code Documentation checks and their build prerequisites; no browser or package unit tests
Story or its local helper Importing stories, plus Storybook contract/type checks
React implementation Package contracts, builds/types/unit tests, docs, Core compatibility and transitively importing stories
Core implementation Package contracts, builds/types/unit tests, docs, Core compatibility and transitively importing stories; no browser test when no Story/preview imports the changed Core module
Package unit tests only Package builds/types/unit tests
Publish/Deployment helpers or their workflows Their tooling tests; no browser suite
Known verification orchestration Verification tooling tests; no browser suite
Quality workflow/runtime configuration Full gate, because Node/Bun/runner/install changes can affect execution
Package version-only change, or lockfile workspace versions with identical resolved dependencies Release/docs checks and build prerequisites; no browser or unit suite
Actual dependency/lock resolution changes Full gate; not treated as release metadata
Global CSS/assets, dependencies, shared configuration, deleted UI modules or unknown inputs Full gate

Story selection follows relative imports/re-exports, literal dynamic imports and React and Core public subpath exports, including NodeNext .js specifiers that resolve to TypeScript sources. Explicit import type/export type declarations are erased at runtime and covered by package typechecks. Preview decorators/loaders/setup are shared by all stories. Generated Paraglide modules are opaque dependencies; changing their locale/configuration inputs selects the full gate. JSX/TypeScript/Vite compiler directives are code inputs even when written in comments. Unresolved imports, computed dynamic imports, glob imports and unsupported mappings fall back to all stories. Cyclic imports are traversed safely. Every selected story still runs in all four browser conditions with the same memory guards and fatal accessibility checks. The selector does not use test-name guesses or silently drop unknown files. Each CI job summarizes its baseline and selection; the local plan is recorded in ignored .verification/quality-plan.json.

Selected Storybook suites run as the React test:browser Turbo task. Its key includes the canonical sorted Story selection, React/Core sources and built entries, Storybook/Vitest configuration, translations/assets, memory-guard helpers, lockfile, Node/Bun/platform/architecture, CI image and relevant environment. A partial suite cannot satisfy another selection or the full suite; only successful task execution is cached. Cache hits suppress old browser output so replayed logs do not look like a fresh browser run. A cache miss runs the unchanged four browser conditions with one worker and three-file batches. Core-only changes still run relevant unit/types and the two-Core matrix; import analysis determines whether UI tests are needed. Unknown inputs, unreadable metadata and changed resolved dependencies remain conservative. Version-only detection compares file contents, not commit messages or filenames. Source quality from a successful ancestor remains usable only when all accumulated changes are covered by the selector.

Use bun run verify:full to request every normal check, or bun run verify:push --dry-run to inspect the local selection and commands. bun run test:consumer remains an explicit packed-consumer check and a mandatory publication check. Public quality success proves the selected changes were checked; it does not claim that every story was rerun for every prose change.

Package-specific stable GitHub Releases use the separate publish.yml workflow. Publishing an appkit-core-vX.Y.Z or appkit-react-vX.Y.Z Release starts verification and stages only that package for npm maintainer approval with 2FA. A manual completion dispatch confirms approved bytes/tags and records the Deployment. See Releasing before publishing a Release; tag pushes alone do not publish npm packages.

The full gate also runs bun run check:react-core: React types, unit tests and packed-consumer SSR/load checks against both its exact registry Core dependency and the new Core archive in separate temporary installations. Network access is required. For Core fixes that must reach React users, add an ID, the first required Core version, a reason and the planned React adoption version to react-core-policy.json. Core preparation can retain the published React dependency until that adoption version; the gate reports the requirement as pending. An outdated dependency blocks CI at the deadline, and bun run check:react-core --release blocks every React publication until all required fixes are adopted, even if compatibility tests pass. See the release procedure.

Biome starts with four blocking rules: unreachable code, unsafe finally blocks, debugger statements and duplicate parameter names. Broader style rules can be added in separately reviewed changes. Use bun run format -- path/to/file.ts when explicitly formatting selected files. Generated outputs, build artifacts and local verification files are outside the Biome source scope; do not exempt application tests or examples from secret scanning merely because a credential is labeled a dummy.

Two narrow source comments suppress Biome compatibility diagnostics: TypeScript’s resolution-mode type import in the AI SDK transport, and a reachable block after a labeled resource-search loop. These suppressions do not change emitted runtime behavior or exclude either whole file from linting. Tailwind directives are enabled in the CSS parser. Review these exceptions when upgrading Biome.

Run bun run storybook from the repository root, then open http://localhost:6006. The command builds public Core and generates React’s translations before starting the isolated Vite workspace. bun run check:storybook checks story types and public import boundaries; bun run build-storybook creates the static preview in packages/appkit-react/storybook-static/.

Stories and their in-memory providers live in packages/appkit-react/stories/, outside the library build and npm payload. Keep both individual stories and composed examples limited to components and dependencies owned by this public repository. Enterprise-only screens stay in their owning private repository.

The preview supplies Appkit CSS, theme state and a separate QueryClient for each selected story. Authentication and form stories add their own mock authentication and in-memory data providers. These fixtures demonstrate isolated UI states; they do not verify hosted authentication, durable persistence or backend access controls. Component descriptions identify their exact public import and required setup. Keep technical descriptions in English and preserve localized UI fixtures.

After changing this guide, run bun run docs:generate and bun run check:docs to update and validate the generated portal copy. Documentation generation needs both package builds: run bun run build first in a clean checkout.

Install the pinned Chromium once with bunx --no-install playwright install chromium (on Linux CI, add --with-deps). Run bun run test:storybook for the full suite. The affected subset is selected by verify:push and the Node 24 quality job. Use bun run verify:full or a manual quality dispatch for full coverage. Publication requires the target commit’s successful main-push Public quality run and does not rerun this browser suite.

Every CSF3 story runs with fatal axe checks in Chromium at 1280×900 and 390×844, in both light and dark mode. The preview and browser runner compile the real Appkit CSS with Tailwind. Palette stories cover default, blue, green, rose, orange and future in both modes, assert the active classes and check semantic text-token contrast (including alpha compositing). Open modal/select/popover stories audit their real portals. Arbitrary host custom colors require host validation.

The HTTP lifecycle stories use browser MSW with unhandled requests rejected, Fetch-based host adapters, shared Zod response schemas and the real public auth, profile, data and file components. Auth/profile, table, form and file-browser compositions cover loading, success, empty, error, signed-out, restricted and privileged states. Native file previews cover loading, text, zero-byte data, network rejection and non-2xx HTTP responses. Interaction stories use real clicks, keyboard navigation and focus return; layout checks measure containment, overflow, tap targets, clipping and overlap. These are isolated component/host-contract tests, not hosted identity, database authorization or seeded-backend E2E evidence.

The runner executes three story files at a time with one worker, sequentially across the four mode/viewport combinations. Each batch gets a fresh Vite/Chromium process tree. Aggregate RSS is capped at the smaller of 3 GiB and one-third of physical memory, the Node heap at 1.5 GiB, and a batch at ten minutes. A resource limit or failed process inspection fails the run; no stories are skipped. The guard kills only its invocation’s process groups, including detached browser descendants, on completion, failure or interruption. Do not start another browser suite in a parallel agent. Reduce batch size/concurrency after a limit failure; do not raise the limit or weaken assertions to obtain a pass. The runner prints measured peaks. node --test scripts/guarded-process.test.mjs checks guard failure and cleanup. The guard and its regression tests are imported github-docs templates; scripts/lib/README.md records their canonical repository, pinned revision, file mappings and update procedure. Maintain shared behavior in github-docs and keep Appkit-specific budgets and batching in this repository’s runner.

Axe cannot replace assistive-technology or design review. No screenshot baselines are generated by this suite; intended visual changes still need human approval.