Development checks
Setup and Git hooks
Use Node.js 22+, pnpm (the version in package.json), and Rust/rustfmt. Install
prek with brew install prek on macOS or
uv tool install prek==0.4.14. The Nix development shell also supplies prek.
pnpm install --frozen-lockfileprek installprek run --all-filesThe hooks also need Go (actionlint and yamlfmt), Python (schema/whitespace checks),
and Cargo/rustfmt on PATH. Docker Compose and hadolint are required when checking
Compose files or Dockerfiles. On macOS, install the missing tools with
brew install go python hadolint, Rust with rustup (including the rustfmt component),
and Docker Compose using Docker Desktop or the Docker CLI’s Compose plugin.
prek manages isolated hook environments; it does not replace these hook-specific tools.
Migrating an existing installation
The configuration remains .pre-commit-config.yaml. There is no need to install
or run the old pre-commit runner. Replace its installed shim explicitly:
prek install --forceprek run --all-files--force overwrites the existing pre-commit shim rather than chaining both runners.
Back up any custom hook first; preserve custom behavior deliberately instead of
blindly overwriting it. If an earlier migration left a pre-commit.legacy hook,
inspect it and remove it only if it is an obsolete runner shim. Repeat installation
for any additional hook types you previously installed (--hook-type pre-push, etc.);
this repository configures only the default pre-commit hook. Remove the old runner
from your package manager after migration. Re-running prek install is idempotent.
Linked Git worktrees share hooks: install once in your main checkout, not as a
worktree-local experiment.
The upstream repositories and Renovate’s pre-commit manager retain their names:
they describe the compatible configuration ecosystem, not the runner we install.
Commands
| Command | Purpose |
|---|---|
pnpm lint / pnpm lint:fix | Biome lint / safe lint fixes |
pnpm format / pnpm format:check | Biome formatting plus Markdown/MDX Prettier formatting / checks |
pnpm format:markdown / pnpm format:markdown:check | Markdown/MDX only |
pnpm check | Svelte and TypeScript diagnostics |
pnpm test | Application tests, recording-boundary regressions, and preview tooling tests |
pnpm build | Regenerate the settings schema with Biome, then build the app |
pnpm docs:build | Build documentation (first run pnpm --dir website install --frozen-lockfile) |
prek run --all-files | All hook checks, including YAML and Rust formatting; may fix files |
Run the checks again after applying fixes. CI uses the same lint/format commands,
boundary tests, type checks, and prek run --all-files. Existing CI actor restrictions
and push/manual triggers are unchanged. Use the manual CI workflow on a branch for
pre-merge checks. Generated/build directories are excluded from Biome/Prettier;
the tracked src/lib/generated/settings.schema.json is intentionally formatted by
Biome and reproduced by pnpm generate:schema.
Formatting and lint coverage
The Biome 2.5.12 migration explicitly opts into experimental full HTML/Svelte/Astro
support. See Biome’s language support.
The tree was formatted and validated with application tests, svelte-check, and
application/documentation builds; re-run these when updating Biome.
| Files | Coverage and limitations |
|---|---|
| JS/JSX/TS/TSX, MJS/CJS | Biome formatting and migrated ESLint rules (exceptions below). Import sorting is disabled to avoid changing side-effect ordering. |
| Svelte, HTML, future Astro | Biome experimental full formatting and embedded JS/TS lint rules. Svelte diagnostics remain in pnpm check. No Astro files are tracked currently; validate new Astro syntax with the docs build. This is not a claim of complete framework/template lint coverage. |
| JSON/JSONC | Biome formatting and recommended JSON lint rules. renovate.json5 became equivalent renovate.json, because Biome does not support general JSON5 syntax (such as unquoted keys). User JSON5 settings are runtime data, not formatting inputs. |
| CSS | Biome formatting and recommended CSS lint rules; Tailwind directives are enabled for docs styles. |
| Markdown/MDX | Prettier only, with prose wrapping preserved and embedded code formatting disabled. No MDX files are currently tracked; future MDX is included in scripts, hooks, and CI. .prettierignore also restricts direct/editor invocations to Markdown/MDX. |
| YAML | Existing yamlfmt hook (both pnpm lockfiles remain excluded), plus YAML/schema/actionlint checks. Run prek, not Biome, for this coverage. |
| Rust | Existing rustfmt hook with --manifest-path src-tauri/Cargo.toml --. |
| Nix, TOML, Python, shell, plist, images, extensionless files | Not formatted by Biome or Prettier. These had no supported built-in Prettier formatter previously. Keep their established conventions; nix fmt remains available. Shell embedded in workflows is checked by actionlint. |
ESLint migration details
biome migrate eslint --include-inspired supplied the rule mappings, which were
consolidated in biome.json. The old TypeScript recommended configuration also
applied to .js files; it disabled checks handled by TypeScript. Those explicit
settings are retained, with the JS-recommended checks restored for MJS/CJS.
Declaration-file namespaces remain allowed for SvelteKit’s App namespace.
noUnusedImports and noUnusedFunctionParameters are enabled separately from
noUnusedVariables (the migration tool only mapped the latter). A regression fixture
checks unused parameters in independently linted MJS scripts, outside TypeScript’s
application checks. The retired recording routes no longer require template-only
Svelte $store import exceptions. The old ESLint configuration did not lint Svelte files.
The migration is not an assertion that every ESLint rule has an identical Biome implementation. The known differences are:
no-delete-varandno-octal: Biome’s parser rejectsdelete identifierand legacy077literals in the repository’s module files. There is no separate lint rule; this does not promise coverage of hypothetical sloppy-mode scripts.no-new-symbol: the old TypeScript configuration disabled this rule. Biome’snoInvalidBuiltinInstantiationrejectsnew Symbol()in MJS/CJS; TypeScript diagnoses it in checked application code.no-unexpected-multiline: no dedicated Biome rule. Formatting checks reveal noncanonical line breaks, but formatting preserves parsed behavior and cannot recover intent after an accidental automatic-semicolon-insertion continuation. Review expression statements across line breaks carefully; type checking is not an equivalent replacement in every case.no-invalid-regexp: literal regex syntax is parsed, but constant-string constructors such asnew RegExp("[")are not validated by Biome. Tests must exercise constructed regular expressions. This is a retained lint gap, not coverage provided by type checking.@typescript-eslint/triple-slash-reference: Biome has no equivalent restriction on valid triple-slash references. Prefer imports; review any new reference directive.@typescript-eslint/no-unused-expressions: Biome’snoUnusedExpressionsis enabled, including rejection of short-circuit expression statements; TypeScript-specific syntax may differ between implementations.- Inspired mappings (
noBannedTypes,noUselessThisAlias,noAssignInExpressions,noTsIgnore) are not option-for-option equivalents. In particular,noTsIgnorebans@ts-ignorebut does not enforce the old description requirement for@ts-expect-erroror the ban on@ts-nocheck; retain those conventions in review. Biome also rejects empty function bodies, so deliberate no-op arrows explicitly returnundefined. Two local suppressions document existing initialization and subscribe-once patterns that Biome otherwise flags.
Session model package boundary
packages/session-model is the private @persyk/session-model workspace package.
Root pnpm check runs its standalone source/test TypeScript checks before the app
check; pnpm test runs its tests before application integration tests. Both remain
part of the existing IPC CI workflow. Run either package command independently with
pnpm --filter @persyk/session-model check or pnpm --filter @persyk/session-model test.
pnpm test:boundaries also checks the package’s runtime source with a TypeScript AST
allowlist: only Zod and relative imports that stay inside package source are allowed,
including type imports, re-exports and dynamic imports. Nonliteral dynamic imports,
reference directives, CommonJS and platform-global identifiers are rejected. The
check deliberately reserves those identifiers even as local names. Tests may use
Node filesystem APIs to read the shared packages/session-model/fixtures corpus;
Rust compatibility tests include the same files. The exports map permits only the
explicit root API, not internal modules or fixture subpaths. These boundary checks
also run in the relevant Git hook.
Recording core boundary
src/lib/recording/core/**/*.ts must not import Tauri, Svelte, OpenAI, stores,
realtime clients, browser adapters, settings, or persistence through the previously
restricted paths. The same seven browser globals remain forbidden. Biome’s
noRestrictedImports and noRestrictedGlobals enforce this boundary; ** patterns
are necessary to retain the old gitignore-style matching of nested import paths.
The restrictions are path-based as before, not a new universal dependency resolver.
pnpm test:boundaries runs the actual configured linter against temporary TypeScript
fixtures. It checks every restricted import family (including type imports,
side-effect imports and re-exports), nested paths, all seven globals, and permitted
capability/injected dependencies. It also checks that browser adapters remain
outside this restriction. The suite runs in pnpm test, CI, and the relevant hook.
Hook compatibility
All original whitespace/EOF, YAML, large-file (500 KB), Taskfile schema, actionlint, Compose, hadolint, yamlfmt, and Rust hooks retain their versions, file filters and arguments. The local ESLint hook is replaced with Biome and expanded to supported code formats; Markdown/MDX and boundary regression hooks are added.
The tracked tree currently has no Taskfile, Compose file, or Dockerfile, so prek correctly reports those three hooks as “no files to check.” Migration validation used valid and invalid fixtures in a temporary Git repository to verify each hook actually passes valid input and rejects invalid input, without changing the file filters or installing hooks into a shared worktree.