49 — Toolchain
The full set of build-and-test tools the Locara project uses. Per ADR 0013, we deliberately pick frontier-but-production-ready tools because build-loop speed matters disproportionately for the AI-author DX.
This document is the operational counterpart to the ADR: what specifically we install, configure, and pin.
TL;DR
JS/TS: Bun (runtime + package mgr + bundler + tests)
Linting: Biome (lint + format)
Styling: Tailwind v4
Website: Astro + Bun
Rust: latest stable + mold linker + cargo-nextest + sccache
Monorepo: Bun workspaces + Cargo workspace
Native: Tauri 2.x
Pinned versions (.mise.toml or .tool-versions)
We use mise to pin tool versions. (Alternative: asdf, manually-installed tools; mise is preferred.)
# .mise.toml at repo root
[tools]
bun = "1.2"
rust = "1.85"
node = "22" # for occasional Node-specific tooling
mold = "2.40"
[env]
RUST_BACKTRACE = "1"
SCCACHE_DIR = "~/.cache/sccache"
Cargo + bun pin themselves via lockfiles; mise pins the higher-level tools.
JavaScript / TypeScript layer
Bun (everywhere)
# Install dependencies
bun install
# Run a script
bun run dev
bun run build
bun run test
# Run a TS file directly (no compile step)
bun run src/main.ts
# Build for production
bun build src/main.ts --outdir dist
# Run tests
bun test
bun.lockb is the lockfile. Commit it. Do not use package-lock.json or pnpm-lock.yaml.
In package.json:
{
"scripts": {
"dev": "locara dev",
"build": "locara build",
"test": "bun test",
"lint": "biome check .",
"format": "biome format --write ."
},
"devDependencies": {
"@biomejs/biome": "^1.9.0",
"@types/bun": "latest"
}
}
TypeScript
Bun handles TypeScript natively — no separate tsc step at runtime. Type-checking is still done via tsc --noEmit in CI:
bun run typecheck # which calls: tsc --noEmit
tsconfig.json:
{
"extends": "@locara/tsconfig/base.json",
"compilerOptions": {
"moduleResolution": "bundler",
"module": "ESNext",
"target": "ES2024",
"strict": true,
"noEmit": true,
"types": ["bun"]
}
}
Biome (lint + format)
biome.json:
{
"$schema": "https://biomejs.dev/schemas/1.9.0/schema.json",
"formatter": {
"enabled": true,
"indentWidth": 2,
"indentStyle": "space",
"lineWidth": 100
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"correctness": { "noUnusedVariables": "error" },
"style": { "useImportType": "warn" }
}
},
"organizeImports": { "enabled": true }
}
Pre-commit hook (via Lefthook or a simple Husky-equivalent):
bun run lint
bun run format --write
CI fails if Biome flags issues.
Tailwind v4
Tailwind 4 uses CSS-based configuration:
/* src/styles.css */
@import "tailwindcss";
@theme {
--color-primary: oklch(0.65 0.2 280);
--font-sans: "Inter Variable", sans-serif;
}
No tailwind.config.js. Theming via CSS custom properties.
Tailwind 4’s Rust-based Oxide engine is the default in 2026.
Astro (website)
website/ uses Astro for the static locara.app site:
cd website/
bun install
bun run dev # dev server
bun run build # static build
Astro is configured to use Bun underneath.
Rust layer
Toolchain pinning (rust-toolchain.toml)
[toolchain]
channel = "stable"
components = ["rustfmt", "clippy"]
profile = "minimal"
We track latest stable; bump quarterly in lockstep with Rust releases. We don’t pin to a specific patch version — letting stable move forward keeps us getting compiler improvements.
Cargo configuration (.cargo/config.toml)
[target.aarch64-apple-darwin]
linker = "/opt/homebrew/bin/clang"
rustflags = ["-C", "link-arg=-fuse-ld=/opt/homebrew/bin/mold"]
[build]
rustc-wrapper = "sccache"
[profile.dev]
opt-level = 0
debug = "limited" # smaller debug info; faster builds
incremental = true
[profile.dev.package."*"]
opt-level = 1 # mild optimization on dependencies; faster runtime in dev
[profile.release]
opt-level = 3
lto = "thin"
codegen-units = 1
strip = "symbols"
mold linker is required; CI installs it via Homebrew.
Tests via cargo-nextest
cargo install cargo-nextest --locked
# Run all tests
cargo nextest run
# Run a single test
cargo nextest run --test-name capability_check
# Filter by package
cargo nextest run -p locara-core
sccache for compilation cache
Configured via RUSTC_WRAPPER env var. Caches build artifacts; speeds up rebuild after cargo clean or new clones.
CI uses sccache with GHA cache backend:
- name: Setup sccache
uses: mozilla-actions/sccache-action@v0.0.4
env:
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: sccache
Tauri 2.x
Per ADR 0001. Tauri’s CLI is Bun-compatible:
bunx tauri dev
bunx tauri build
Pinned via Cargo.toml workspace dependencies + package.json devDependency.
CI configuration (GitHub Actions)
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: macos-14 # Apple Silicon runner
steps:
- uses: actions/checkout@v4
- name: Install mise
uses: jdx/mise-action@v2
- name: Setup sccache
uses: mozilla-actions/sccache-action@v0.0.4
- name: Install Bun deps
run: bun install --frozen-lockfile
- name: Lint + format check
run: |
bun run lint
bun run format --check
- name: Type-check
run: bun run typecheck
- name: Test (TS)
run: bun test
- name: Build Rust workspace
run: cargo build --workspace --release
env:
RUSTC_WRAPPER: sccache
- name: Test (Rust)
run: cargo nextest run --workspace
CI is fast: typical PR runs in <2 minutes thanks to sccache + parallel test runners.
Performance targets (build loop)
These are the goals the toolchain choices enable:
| Operation | Target |
|---|---|
bun install (warm cache) | < 5s |
bun install (cold) | < 30s |
bun run typecheck | < 5s |
bun test (small package) | < 1s |
cargo build (warm, single crate) | < 3s |
cargo build --release workspace (CI, sccache hit) | < 2 min |
cargo nextest run (small crate) | < 1s |
locara dev cold start | < 1.5s |
locara dev HMR refresh | < 200ms |
| Biome lint full repo | < 2s |
If any target slips by 2x in CI, we investigate as a regression.
Fallback strategies
If a frontier tool blocks development:
| Tool | Issue scenario | Fallback |
|---|---|---|
| Bun | runtime crash / Node-incompat | Pin previous Bun; report upstream; if persistent → switch to pnpm + Node temporarily |
| Bun bundler | bundler issue | Switch to Vite or Rolldown for affected package |
| Biome | rule we need missing | Add custom rule; or temporarily ESLint for that rule |
| Tailwind 4 | engine bug | Pin Tailwind 3 temporarily |
| mold | link failure | Default ld linker (slower but works) |
| cargo-nextest | test infra issue | cargo test (slower but works) |
| sccache | cache corruption | Clear cache; rerun |
Switching back is documented as 1-day work. We don’t accept tools that would take a week to switch off — that’s a sign of risky lock-in.
Adding a new tool
When a contributor proposes a new tool:
- Justification. Why is this better than what we have?
- Compatibility. Does it play with the rest of the stack?
- Bus factor. Who maintains it; how stable is the project?
- Fallback. Can we exit cleanly if it doesn’t work?
- ADR. If it’s a meaningful addition, write an ADR.
Casual tool changes (a new test helper, etc.) don’t need this rigmarole. Toolchain-level changes do.
Onboarding (developer install)
A new developer’s first-time setup:
# Install mise
curl https://mise.run | sh
# In the repo root
mise install # installs Bun, Rust, mold, etc.
bun install # installs JS deps
cargo build --workspace # builds Rust crates
bun test # runs TS tests
cargo nextest run # runs Rust tests
locara doctor # confirms environment
5–10 minutes start to finish on a clean Mac.
OS support
Toolchain works on:
- ✅ macOS (Apple Silicon — primary target)
- ✅ macOS (Intel — for older Macs; lower priority)
- ✅ Linux (x86_64 — for CI + curious devs)
- 🟡 Linux (arm64 — partial; some tools)
- 🟡 Windows (Bun + Rust work; mold doesn’t; would need lld) — phase 4+ if we go cross-platform
For app distribution this is moot — Locara apps are macOS-only in v1. The toolchain just needs to work on developer machines, which are mostly Mac.
Open questions
- (open) Should we adopt rolldown when stable? Probably yes, if it ships before phase 1 ends.
- (open) Use Bun’s native test runner exclusively, or also Vitest for projects that want React-component-test ergonomics? Probably Bun-native; revisit if Vitest’s component-testing story is materially better.
- (open) For the website, Astro vs raw Bun-bundled HTML? Astro for content management ease; revisit if it adds friction.
- (open) Should we ship a
locara doctorcheck that validates toolchain version compatibility? Yes — already partially in 06-cli.md; expand to check all the frontier tools.
Cross-references
- ADR 0001 — Tauri over Electron
- ADR 0002 — llama.cpp v1; MLX in v2
- ADR 0008 — SQLite + sqlite-vec storage
- ADR 0009 — Apache 2.0 license
- ADR 0013 — Frontier toolchain
- 17-repo-layout.md — repo structure
- 16-build.md — build pipeline
- 30-testing-strategy.md — testing strategy
- 44-templates.md —
locara inittemplates