Skip to content

Contributing

Musubi is open source under the MIT licence and welcomes contributions — bug fixes, features, tests, or docs.

  • Check the GitHub issues — someone may already be on it.
  • For larger changes, open an issue first to agree on the approach.
  • Keep PRs focused: one thing per pull request.
  • Review the current codebase audit before working near a recorded risk; avoid folding unrelated audit cleanup into a focused change.

Follow Running Locally to get the full stack up.

New to the codebase entirely? Take the Codebase Onboarding course first — a guided afternoon that runs the app, traces one request through every layer, and ends with a recipe for your first change.

Otherwise: read the Architecture Overview — it gives you the one idea (events are linked into calendars, not owned by them) that the whole codebase assumes. Then read the page for the area you’re touching:

pnpm workspaces + Turborepo:

Workspace What lives there
apps/api Express API — routes, permissions, sync engine
apps/client Expo app (Expo Router)
packages/db Drizzle schema, queries, PostgreSQL migrations
packages/types Shared Zod schemas + types — change data shapes here first
packages/calendar Recurrence (RRULE) expansion + date helpers — logic only
packages/auth Better Auth config
packages/config Environment loading
packages/docs This documentation site

Run the repository baseline from the root:

Terminal window
pnpm check

It performs client/API typechecking, all twelve assertion-based self-checks, and client lint before the production docs build. Lint has zero errors and a locked budget for 108 existing React Compiler/hook warnings; a new warning fails the command when it increases the total. Fix warnings when practical and lower the budget with them—never raise it to make a PR pass. The exact rationale and remaining migration are tracked in the audit. For the individual commands and self-check inventory, see Command reference.

Pull requests also run a clean frozen install, verify that generating Drizzle migrations leaves no diff, and build the API Docker image. The same workflow gates manual releases.

After editing packages/db/src/schema.ts:

Terminal window
pnpm db:generate # generate a migration from the schema diff
pnpm db:migrate # apply pending migrations

Commit the generated SQL in packages/db/drizzle/ with your change. See the migration workflow for the gotchas (renames, cascades, forgetting to generate).

TypeScript throughout. The conventions reviewers check:

  • Stores are API-first — Zustand stores in apps/client/store/ write state/cache only after the API call succeeds; components read from stores, not local server state. Merge, don’t replace on calendar updates (SSE payloads omit fields).
  • UI primitives, not ad-hoc — use Tap/Btn/Empty/Toast/confirm() from components/ui/; a bare Pressable or hand-rolled dialog gets flagged.
  • Theme at render time — read colors/fonts from constants/theme.ts inside the component, never into a module-level constant (it freezes to the first-loaded theme). See theming.
  • Reanimated v4, not the legacy Animated API. In the custom calendar, keep positions in shared values and use runOnJS for React setters.
  • No inline magic numbers — name them (a layout.ts-style tuning block or constants/).
  • Change data shapes in packages/types first, then thread the type through both apps.
  1. Fork and branch from main.
  2. Make your change; keep it focused.
  3. Run pnpm check and the closest targeted verification.
  4. Test on iOS and Android if the change touches the native client; deliberate platform branches make one simulator insufficient.
  5. Update code-coupled documentation, environment examples, and migrations.
  6. Open a PR with a clear description of what changed, why, risk, and verification evidence.

Open a GitHub issue or discussion before spending time if you’re unsure about the approach.

Changing this site? Follow Writing documentation for information architecture, Starlight conventions, and browser QA.