Thanks for picking up Roomy. The bar for this codebase is "real users hit it daily" — every change should preserve that. The notes below cover what's enforced automatically vs reviewed manually, and the few conventions we ask new contributors to follow.
- Node.js 23.x — pinned by
.nvmrcandengines. Use volta/fnm/nvm/mise/asdf. - Docker — rootful or rootless on Linux, Docker Desktop on macOS. Or nerdctl + containerd if Docker can't coexist.
- An API key is not needed for development — the free
opencode/big-picklemodel handles everything that needs an AI.
git clone git@github.com:bgrgicak/Desk.git
cd Roomy
npm install # also installs the pre-commit hook
npm run dev # boots roomy-server + ViteOpen http://localhost:5173/, sign in as roomy / the
ROOMY_SEED_PASSWORD (default change-me-before-first-boot).
Runs against every commit, on every file you've staged.
| Check | Why it exists |
|---|---|
npm run typecheck |
Catches type errors before they reach CI. |
vitest run (unit + component, no integration, no e2e) |
Real Postgres / Docker / AI tests are gated to CI because they need a workstation set up for them; this gate is fast and runs everywhere. |
If a hook fails, fix the underlying issue rather than bypassing with
--no-verify. The hook is intentionally narrow so it stays fast — the
heavier checks live in CI.
Triggers on push to trunk and on pull requests against it.
| Job | What it covers |
|---|---|
| Typecheck | npm run typecheck across every workspace. |
| Vitest (unit suite) | Pure-logic tests with no external dependencies. |
| Vitest (integration suite) | Real SQLite, real Docker, real at / crontab. Auto-detects backend availability. |
| Playwright e2e | Real roomy-server + Vite preview. Drives the UI end-to-end against the fake sandbox driver. |
| Prepare sandbox image | Builds roomy/sandbox:v1 once; later jobs download the saved image instead of re-building. |
| Build desktop app (macOS) | Validates the Electron wrapper still installs / packages cleanly. |
The aggregate test job fails unless every required parallel job
succeeds. Branch protection on trunk should require this aggregate
check before merge.
npx knipRuns against the committed knip.json config. Not yet wired into CI as
a blocking gate — contributors are welcome to run it locally before
landing a feature and either remove what it flags or add a justification
in knip.json's ignore list.
A pull-request reviewer should be able to answer "yes" to each item below before approving. The PR template includes this list as a checklist.
- Does the change include a test for the behaviour it touches?
- New HTTP route: rate-limited where bruteforce or abuse is possible
(look at
auth/rateLimit.tsfor the helpers)? Auth-checked? Ownership-checked? Input validated with the existing Zod patterns? Returns the standard{code, message, details?}shape on error? - New sandbox env var: provider key? Logged to
provider_key_access_logvialogKeyAccess(...)? - New external service integration: connection stored via the generic connector store, not a one-off table?
- No raw
console.*in server source — log via the structured logger if you need persistence, ortoast.error()in the app. - Source file under 600 lines, with a deliberate justification if not.
- New flaky test: root-cause fix or
it.todo()with a tracking issue link. Retries should be the exception, not the strategy. - New form in the app: uses
react-hook-form+ Zod schema (work in progress — once the pattern is in place, the rule is uniform). - New mutation site:
.unwrap()wrapped in a.catch()that surfaces the error to the user (toast or banner). Silent failures are a bug.
- TypeScript strict mode is on everywhere. No
any; if a third- party type makes that hard, narrow with a type guard rather than a cast. - No barrel files beyond each package's existing
src/index.ts— they make tree-shaking unpredictable and slow tsc. - Prefer Zod parsing at boundaries (HTTP handlers, sandbox tokens) and trust the parsed type from there on. Don't double-validate.
- No commented-out code in commits. If you're unsure, delete it and reach for git history later.
- No
console.*inpackages/server/**/src/. Use the structured logger. The app and tests are exempt for now. - Errors that hit the user are toasts, not exceptions logged to the
devtools console.
extractApiError(err)in@/lib/api-errorpulls the server'sdata.messagewhen present.
- Commit messages: imperative mood, first line ≤ 70 chars ("Add X" / "Fix Y" / "Remove Z"). Body explains why the change matters and why this approach over the obvious alternative.
- Pull requests should land as a series of focused commits rather than one giant squash — it makes review and bisecting easier.
- Don't rebase a branch other people are reviewing without warning them in the PR thread.
- Architectural questions: start a thread in the PR description or open an issue. Move fast, but not so fast that an architectural decision happens without daylight.
- Stuck CI: the workflow logs are linked from each PR. Reviewers expect a green build, so debug failures before requesting review.
- Anything else: open an issue — even if it's only to say "I'm not sure where this belongs."
Thanks for keeping the bar high.