# Contributing to Crewship Thanks for considering a contribution. This file is the short-form workflow. ## Before you start - **Open an issue first** for anything non-trivial so we can sanity-check scope before you spend time. Bug fixes with a reproducer are welcome without prior discussion. - **One feature branch per change** — never work directly on `main`. Branch naming: `feat/`, `fix/`, `chore/`. - **Claim the issue before your first commit** — `scripts/claim-issue.sh `. Several sessions work this repo at once from the same GitHub account; the claim comment is the only thing that tells them apart. See [Claiming an issue](#claiming-an-issue-before-you-work-it). ## Local development ```bash pnpm install cp .env.example .env.local # set NEXTAUTH_SECRET + ENCRYPTION_KEY ./dev.sh start # backend :8080 + frontend :3001 ``` Other `./dev.sh` subcommands: `stop`, `restart`, `status`, `seed`, `nuke`, `logs`. Never start the services manually. The long-form explanations that used to live in this file moved to [docs/development/](docs/development/README.md): the [`web/out` embed](docs/development/web-out-embed.md), [build identity in a worktree](docs/development/build-identity-in-worktrees.md), [Go toolchain pinning](docs/development/go-toolchain-pinning.md), [the Sentry vitest alias](docs/development/sentry-vitest-alias.md) and [dependency drift](docs/development/dependency-drift.md). ## CI and release verification Use `bash scripts/verify.sh quick` before pushing (`go` and `full` add the full language suites). CI, Security and CodeQL expose stable aggregate results; a planned skip is checked against the change plan rather than accepted merely because GitHub renders it green. The real image build now runs as part of CI Result, including for frontend changes. The publication flow, exact-SHA gates, artifact identities, local commands and runtime credential requirements are documented in [the CI/CD implementation runbook](docs/runbooks/ci-cd-implementation-2026-09-11.md). ## Verify any change Run the relevant checks locally before pushing. CI routes checks by change type (see Pull requests below); unknown paths and main pushes run the full suite: ```bash go test ./... -count=1 -timeout 40m && go vet ./... # Go: must pass pnpm lint && pnpm build # Frontend: must pass for UI changes ``` The full Go suite can exceed the default ten-minute timeout on shared machines. The explicit timeout is a ceiling, not a reason to ignore a failed or hanging test. For preliminary checks and script side effects, see the [script catalog](scripts/README.md); for code placement, see the [repository layout](docs/development/repository-layout.md). The `cmd/crewship` suite scrubs every `CREWSHIP_*` variable from its own environment before running, so a shell that exports `CREWSHIP_PROFILE` or `CREWSHIP_SERVER` for a dev instance can no longer redirect the tests at your live server. Tests that need one of those vars set it themselves. After that scrub, the harness sets `CREWSHIP_NO_SLUG_CACHE=1` so CLI child processes bypass the shared workspace slug cache: an ephemeral test server can reuse another fixture's URL and slug with a different workspace ID. Disk-cache behavior is tested explicitly in `internal/cli/slugcache_test.go`. For UI changes, also exercise the affected feature in a browser before declaring it done. Type checking and tests verify code correctness, not feature correctness. ## House rules (the short list) - **`pnpm` only** — never `npm` or `yarn`. - **Migrations are one `.sql` file each** in `internal/database/migrations/` — the directory *is* the registry, versions strictly ascending. Go-side migrations only for cases that genuinely need schema discovery or table rebuilds. **Never run `prisma migrate`** — Prisma is TypeScript types only. Full detail: [`internal/database/migrations/README.md`](internal/database/migrations/README.md). - **No new API routes in `app/`** — the static export drops them in prod. All API routes go in `internal/api/`. - **Driver name is `"sqlite"`** (not `"sqlite3"`). - **Sidecar UID 1002, agent UID 1001** — security boundary; do not change. - **No `Co-Authored-By` lines in commits.** - **Never amend after a pre-commit hook failure** — make a new commit. - **A detached goroutine in `internal/api` registers with `beginBackgroundWork`** (`internal/api/background.go`) — work that outlives its request also outlives the test that drove it. Background and the `storageDir(t)` rule: [house-rules background](docs/development/house-rules-background.md). - **`Commands()` on a shared Cobra command is a *write*.** Never execute a shared command from a parallel test; build a local command tree. Why: [house-rules background](docs/development/house-rules-background.md#commands-on-a-shared-cobra-command-is-a-write). - **`go test -race ./internal/api/` needs `-timeout 40m`** — the package takes ~23m under the race detector and the default 10m kills it mid-test in a way that looks exactly like a failure. Details: [house-rules background](docs/development/house-rules-background.md#go-test--race-internalapi-needs--timeout). - **Don't hash at production strength in a test.** Never write a literal bcrypt cost at a call site — guard tests reject it. Details: [house-rules background](docs/development/house-rules-background.md#dont-hash-at-production-strength-in-a-test). ## Frontend data fetching New or migrated client data hooks use **@tanstack/react-query** (wired in `components/providers.tsx`) with `apiFetch` from `lib/api-fetch.ts`, not hand-rolled `fetch` + `useState`; reference implementations: `hooks/use-dashboard-data.ts` and `hooks/use-inbox.ts`. The full conventions — query keys, transport, freshness via realtime events, error mapping, and test setup — live in [docs/development/frontend-data-fetching.md](docs/development/frontend-data-fetching.md). ## Commit messages We use [Conventional Commits](https://www.conventionalcommits.org/). The type and scope drive changelog and release tooling, so they are checked at review time. ``` ():