Contributing¶
Getting a dev environment¶
You need Node.js 20.19 or newer (the project is developed on 24) and pnpm.
A Rust toolchain (rustup) is only needed if you work on the desktop or
mobile shells.
pnpm install
pnpm dev # API on :3000 + web app on :5173, with hot reload
pnpm seed # optional: demo courses and study fixtures
If you use Nix, run nix develop for the whole toolchain, pinned and matching
the CI: Node, pnpm, Rust, and on Linux the Tauri webkit2gtk libraries. With
direnv it activates on its own through the checked-in .envrc. See
flake.nix.
For the desktop shell:
cd apps/shell
pnpm tauri dev
Repo layout¶
apps/web Svelte 5 SPA, where all the study features live
apps/api NestJS API: auth, files, AI proxy, sync log
apps/shell Tauri shell (desktop and mobile) with the LAN sync plumbing (Rust)
packages/ai-core provider-agnostic AI calls
packages/design-system UI components and tokens
docs this documentation (mkdocs)
Read the architecture page before touching the data layer. The local-first model, with the CRDT documents as the source of truth and the server as an optional peer, shapes almost every design decision here, and the "where to start reading" table on that page maps the important files.
Quality gates¶
All of the following must pass before a change lands. CI runs the same set.
pnpm lint # eslint across the monorepo
pnpm --filter @synapse/web check # svelte-check + i18n compile
pnpm --filter @synapse/web test # vitest (unit)
pnpm --filter @synapse/api test # jest (unit + integration)
pnpm --filter @synapse/web test:e2e # playwright, seeds its own db
cd apps/shell/src-tauri && cargo test
The e2e suite boots the API on a fresh, migrated and seeded database on every run, and drives the app through its real repositories rather than test-only routes. The accessibility spec runs axe WCAG scans on every main page.
Conventions¶
-
Commits follow Conventional Commits:
type(scope): summary, where type is one of feat, fix, docs, style, refactor, perf, test, build, ci, chore or revert, and scope is optional (web,api,shell,design-system). Keep the summary short, lowercase and imperative. -
User-facing strings go through Paraglide (inlang). The catalogs are
apps/web/messages/en.json,fr.jsonandpt-BR.json; add every new message to all three. Paraglide compiles them into typed functions undersrc/paraglide/(this runs automatically withcheck,testand the dev server), and components call them asm.your_message_key(). A key missing from a locale fails the compile, so forgetting a translation is caught early. - E2E selectors are role-based (
getByRole) rather than CSS classes. - The web app never reads study data over HTTP. Pages call the
repositories in
apps/web/src/lib/data/repos/. If a feature seems to need a new server endpoint, check the architecture first, because it usually needs a document field instead. - Errors surface as
neverthrowresults on the client, and unhandled rejections are treated as bugs. - Documentation lives in
docs/. When you change behaviour, update the page that covers it. Install the tooling once withpip install -r docs/requirements.txt, then preview the site withpnpm docs:serve(and build it withpnpm docs:build). These wrapmkdocsand first build the in-browser demo intodocs/try/app, so the "Try it" page works; a baremkdocs serveskips that step and the demo will 404.
AI assistance¶
AI tools were used to help build this project, and they are welcome for contributions too, under the same discipline the codebase was built with: plan first, then generate, then review. Concretely, AI here automates boilerplate and test scaffolding after a carefully crafted plan, and every produced result is read, questioned and tested by a person before it lands, with quality, security and accessibility in mind. The decisions that shape the project, such as the data model, the security model, the dependencies and the UX, are made by humans. Nothing is merged blindly.