toolchain
Dorkroom's lint/format/typecheck/dependency toolchain — oxlint, Biome, the side-by-side TypeScript 7 (compiler) / TypeScript 6 (API) split, and the two-tier dependency pinning policy. Use when changin
SKILL.md.claude/skills/toolchain/SKILL.md
GitHubDorkroom toolchain
Lint and format
- Lint with
oxlint, format with Biome. Each package'slintrunsoxlint(lint, config in.oxlintrc.json) thenbiome check --linter-enabled=false .(formatting + import sorting only — Biome is no longer the linter).bun run formatwrites Biome formatting. - Several jsx-a11y rules are set to
warn(consumer-labelled input primitives, intentional modal backdrops) rather thanerror; fix genuine findings and tighten toerrorover time.
Type-aware lint
- Type-aware rules (
no-floating-promises,no-misused-promises, etc.) run viabun run lint:types(oxlint --type-aware, powered byoxlint-tsgolint). - Not in the CI gate yet: tsgolint can't parse a couple of the project tsconfigs (
tsconfig-error) and it surfaces ~200 findings to triage. Run it locally to catch real async bugs; wire it into the gate once those are resolved.
Typecheck and build
Two TypeScripts are installed on purpose. 7 compiles; 6 provides the API. Do not collapse them.
- Typecheck/build run on stable TypeScript 7, installed as
typescript-7(an npm alias fortypescript@^7.0.2— the Go compiler). Every package'sbuild/typecheckscript invokes it by explicit path:node ../../node_modules/typescript-7/bin/tsc. typescriptis deliberately held on 6.x — it is the compiler API, not the compiler. TypeScript 7.0 ships no programmatic API (it returns in 7.1), and@vercel/nodedoesrequire.resolve('typescript')against the project to compileapi/**/*.tsat deploy time, so a 7.xtypescriptsilently breaks the serverless build (ts.sys/ts.readConfigFileareundefined). Vite, Vitest, and the editor language service resolve the same package. This is the side-by-side layout from Announcing TypeScript 7.0.renovate.jsondisablestypescriptmajors so the 6→7 bump stops being re-proposed; lift the hold and collapse the two packages once 7.1 restores the API (Microsoft's stated window is 3–4 months from the 7.0 RC, so roughly Q4 2026).- Renovate owns every dependency in this repo (
renovate.json). It replaced Dependabot, whose bun-ecosystem support had three disqualifying failure modes: it silently skips aliased packages (dependabot-core#15847 — how thetypescript-7pin sat on the movingrcdist-tag from #133 until caught manually), it unifies a version across every workspace manifest (how PR #187 rewrote apps/mobile's tailwindcss from v3 to v4 while calling itself a patch bump), and it bumps react-dom without react (issue #208 — always fails CI). Things about the config that are load-bearing:- The typescript-7 rule matches on
matchDepNames: ["typescript-7"], notmatchPackageNames. The alias resolves topackageName: "typescript", so amatchPackageNamesrule would also match the 6.x compiler-API pin and propose exactly the 6→7 bump that breaks the Vercel build. - Global
rangeStrategy: "bump": under the defaultreplacestrategy an in-range release like typescript 7.1.0 would satisfy^7.0.2and produce no PR — and 7.1 is precisely the release we are waiting for.bumpmoves the floor, so releases arrive as visible PRs. - Global
minimumReleaseAge: "7 days"mirrors thebunfig.tomlsoak gate, so Renovate never proposes a versionbun installwould refuse. - react/react-dom (+ their @types) are grouped into one PR (
groupName: "react") — they must be the exact same version or react-dom refuses to boot. - Expo SDK-managed native modules are disabled entirely:
expo install --fixowns those versions, and nothing in CI builds the native app to catch a bad bump. - GitHub's "automated security fixes" (Dependabot security PRs) are disabled at the repo level;
Renovate's
vulnerabilityAlerts/osvVulnerabilityAlertscover security updates instead. - Renovate requires the GitHub App to be installed on the repo; the config file alone does nothing. If updates ever go quiet for a long stretch, check that first.
- The typescript-7 rule matches on
- Don't reach for the
@typescript/typescript6compat wrapper that the announcement recommends (typescript@npm:@typescript/typescript6). Under bun 1.3.11 it self-destructs: the wrapper's@typescript/old→npm:typescript@^6dependency dedupes against thetypescriptalias key and resolves to the wrapper itself, sorequire('typescript')returns an empty module. Plaintypescript@^6.0.3gives the same API plus the lib files andtsserverthe wrapper only proxies. bunx tscis TypeScript 6. Both packages ship atscbin and bun linkstypescript's. Use the explicitnode node_modules/typescript-7/bin/tscpath (as the package scripts do) whenever you mean the 7.x compiler.
Dependency pinning
Pinning is two-tier:
- Toolchain that shapes the production bundle (vite, vitest, tailwindcss, jsdom, etc.) is pinned exact.
- Everything else uses
^ranges. - Babel (
@babel/*) stays on ranges — Vite 8/Oxc does the actual transforms, so Babel is dev-only tooling here. - The
bunfig.tomlminimumReleaseAgegate (7 days) backstops the ranges. To install something published in the last week (e.g. an urgent CVE patch), runbun install --minimum-release-age 0. - The gate currently has no exemptions, and it is worth keeping it that way.
minimumReleaseAgeExcludesexisted only whiletypescript-7tracked the movingrcdist-tag, which could resolve to a same-day pre-release; it was dropped once that pin went stable. Adding a package back to that list punches a permanent hole in the supply-chain soak gate — prefer the one-off--minimum-release-age 0flag.