Skip to content

Development environment

Development environment

Per ADR-0014, all development happens in WSL2, with the repository inside the WSL filesystem.

The /mnt/c rule

The repository must live at ~/code/tabletop-tracker, never under /mnt/c/....

Cross-filesystem I/O through the 9p protocol is slow, and file-watching across it is unreliable. Dev servers miss changes, node_modules operations crawl, and pnpm install takes minutes instead of seconds.

Both symptoms present as flaky tooling rather than as a filesystem problem — which is precisely why it burns agent loop iterations. An agent debugging a dev server that “doesn’t pick up changes” will look at the code for a long time before suspecting the filesystem.

Windows files remain reachable at /mnt/c for the occasional copy. Nothing in the working set lives there.

Prerequisites

ToolNotes
WSL2 + Ubuntuwsl --install from an elevated PowerShell. Verify with wsl -l -v — must say version 2
gh CLIInstall and authenticate before cloning — see below. The repo is private, and the work queue depends on gh
NodeVersion pinned in .nvmrc, installed inside WSL
pnpmVia corepack enable — never a global npm install. Needs sudo when Node is a system install
Docker runtimeDocker Desktop or Rancher Desktop, with WSL integration enabled for this distro — see the trap below
WranglerVia the workspace, not globally

Before you clone: authentication

WSL shares nothing with Windows — separate filesystem, separate credential store. A gh login on the Windows side does not carry over, and GitHub has not accepted passwords for git operations since 2021. A fresh Ubuntu has no gh at all, so git clone on this private repo fails before anything else can start.

Terminal window
# Install gh (Ubuntu)
sudo mkdir -p -m 755 /etc/apt/keyrings
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
| sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null
sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
| sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
sudo apt update && sudo apt install gh -y
# Authenticate. Say YES to "Authenticate Git with your GitHub credentials".
gh auth login
gh auth setup-git
git config --global user.name "Your Name"
git config --global user.email "you@example.com"

No SSH key is required. gh auth setup-git installs a host-scoped credential helper, so git config --get credential.helper returns empty while auth works fine — the key is credential.https://github.com.helper. That looks like a misconfiguration and isn’t. SSH is a valid alternative, just more to manage.

The Docker trap

Three separate things must be true, and the failure mode is identical for all three:

failed to connect to the docker API at unix:///var/run/docker.sock
  1. A Docker runtime is installed — Docker Desktop or Rancher Desktop.
  2. WSL integration is enabled for this distro. Rancher Desktop: Preferences → WSL → Integrations. Docker Desktop: Settings → Resources → WSL integration.
  3. The application is actually running. This is the one that catches people.

The reason all three look the same is that the integration checkbox only puts the CLI binaries on your PATH — reached via /mnt/c. The daemon lives elsewhere (for Rancher Desktop, in a separate rancher-desktop WSL distro that starts with the app). So with the app closed you still get:

  • command -v docker → resolves, to a path under /mnt/c/Program Files/...
  • docker info → prints a full Client: section with versions and plugins
  • docker compose version → prints a version
  • Every actual command → socket error

Nothing in that list tells you the daemon is down. Check the distro rather than the CLI:

Terminal window
wsl -l -v # from PowerShell: is rancher-desktop Running or Stopped?
ls -l /var/run/docker.sock # in WSL: absent means no daemon, regardless of CLI

Also confirm the container engine is dockerd (moby), not containerd. Rancher Desktop ships both docker and nerdctl on the PATH regardless of which engine is selected, so their presence proves nothing — but ADR-0011 and WI-006 use docker compose, which needs dockerd.

Verify before you need it, not at WI-006:

Terminal window
docker run --rm hello-world

Setup

Terminal window
# In WSL, not PowerShell. Authentication above must be done first.
mkdir -p ~/code && cd ~/code
git clone https://github.com/psifive/tabletop-tracker.git
cd tabletop-tracker
# Enable the pre-push hook that blocks pushes to main (ADR-0018).
# A fresh clone has NO protection until this is set — main is not
# protected server-side on the free tier.
git config core.hooksPath .githooks
sudo corepack enable # sudo: the shim is written to /usr/local/bin
pnpm install
# Test database — ADR-0011. Requires the Docker trap above to be resolved.
docker compose -f packages/db/docker-compose.yml up -d
pnpm --filter db migrate
pnpm --filter db seed
# Confirm the gate is green before starting work
pnpm gate

A green pnpm gate on a fresh clone is the M0 acceptance criterion. If it is red, that is a bug in the foundation, not something to work around.

Editors

Connect over the WSL remote — VS Code Remote - WSL or equivalent. Opening the project through \wsl$\... or \wsl.localhost\... from Windows-side tooling reintroduces the 9p penalty the whole decision exists to avoid.

Everyday commands

Terminal window
pnpm gate # the full gate, as CI runs it
pnpm turbo test --filter=api # fast inner loop
pnpm --filter docs dev # the docs subsite locally
pnpm --filter api dev # Wrangler dev
pnpm --filter db studio # Drizzle Studio

Secrets

Local secrets live in .dev.vars (Wrangler) and .env.local, both gitignored. Nothing real is ever committed — see security-and-secrets.md. Accounts and tokens are provisioned by a human under WI-H04.