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
| Tool | Notes |
|---|---|
| WSL2 + Ubuntu | wsl --install from an elevated PowerShell. Verify with wsl -l -v — must say version 2 |
gh CLI | Install and authenticate before cloning — see below. The repo is private, and the work queue depends on gh |
| Node | Version pinned in .nvmrc, installed inside WSL |
| pnpm | Via corepack enable — never a global npm install. Needs sudo when Node is a system install |
| Docker runtime | Docker Desktop or Rancher Desktop, with WSL integration enabled for this distro — see the trap below |
| Wrangler | Via 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.
# Install gh (Ubuntu)sudo mkdir -p -m 755 /etc/apt/keyringscurl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \ | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/nullsudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpgecho "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/nullsudo apt update && sudo apt install gh -y
# Authenticate. Say YES to "Authenticate Git with your GitHub credentials".gh auth logingh 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- A Docker runtime is installed — Docker Desktop or Rancher Desktop.
- WSL integration is enabled for this distro. Rancher Desktop: Preferences → WSL → Integrations. Docker Desktop: Settings → Resources → WSL integration.
- 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 pluginsdocker 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:
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 CLIAlso 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:
docker run --rm hello-worldSetup
# In WSL, not PowerShell. Authentication above must be done first.mkdir -p ~/code && cd ~/codegit clone https://github.com/psifive/tabletop-tracker.gitcd 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/binpnpm install
# Test database — ADR-0011. Requires the Docker trap above to be resolved.docker compose -f packages/db/docker-compose.yml up -dpnpm --filter db migratepnpm --filter db seed
# Confirm the gate is green before starting workpnpm gateA 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
pnpm gate # the full gate, as CI runs itpnpm turbo test --filter=api # fast inner looppnpm --filter docs dev # the docs subsite locallypnpm --filter api dev # Wrangler devpnpm --filter db studio # Drizzle StudioSecrets
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.