cell, project, container, stack, module — defined in .claude/rules/vocabulary.md, which loads every session. Do NOT use session or workspace as devcell-layer terms.
- Do NOT create commits automatically. Always ask the user to commit.
- Do NOT push to remote unless the user explicitly asks.
Every behavioral change to cmd/, internal/, or nixhome/modules/llm/*.nix lands with a test that was failing before the change. Write the failing test first, implement the minimum to pass, then refactor.
Applies to: a new flag, env var, TOML key, or CLI subcommand (cmd/*_test.go); a new internal/* function with observable behavior (same package); a new MCP server, system-prompt source, or runner argv field (internal/runner/*_test.go).
No new test required for: pure refactors, docs, dependency bumps, nix module additions (nixhome now lives in devcell-sh/community-home), or entrypoint shell fragments (covered by test/).
- Nix is owned by the
devcelluser, home at/opt/devcell— stable, never remounted. - The session user is
$HOST_USER, home at/home/$HOST_USER, created at startup by the entrypoint. - Nix profile path is
/opt/devcell/.local/state/nix/profiles/profile— home-manager's native path, updated on everyhome-manager switch. - The entrypoint copies
/opt/devcell/dotfiles to/home/$HOST_USER/withsed "s|/opt/devcell|$HOME|g"to redirect write paths. - Use
ln -sfT(notln -sf) when replacing a symlink-to-directory;-Tprevents creating the link inside the target. ENV USER=devcellis required in the nix stage —nix.shchecks[ -n "$USER" ]and silently no-ops if empty.$HOME/.config/nix/nix.confmust carryexperimental-features = nix-command flakesat BUILD time.
Do NOT use ARG TARGETARCH=amd64 — the docker driver doesn't set it for host-platform builds. Use ARCH=$(uname -m) in RUN steps.
Nix modules (nixhome) now live in the standalone devcell-sh/community-home repo. Edits to .nix files in this repo are limited to flake.nix (the Go package build).
Escaping inside writeShellScriptBin (''...'' strings) is the usual culprit:
${VAR}must be''${VAR}(otherwise Nix interpolates it)''(empty shell string) must be''''$VARwithout braces passes through as-is
The CI Deploy Site workflow compiles all cmd/*.go together with cmd/gendoc.go. Three things must hold or go build exits 1:
- Run
go mod tidy && go build ./...after any dependency or import change, and commit the result. A green local build is NOT enough — CI starts from a clean module cache, so a missinggo.sumentry only surfaces there. - Build-time-only tooling deps must stay anchored in
cmd/tools.go(//go:build tools).cmd/gendoc.gois//go:build ignore, sogo mod tidycan't see itscobra/docimport and would prune the transitive deps. Anchor any other build-ignored tool's deps there too. docs/is gitignored (swagger output) butcmd/serve.goimports it, so any workflow compilingserve.gomust runtask swagger:generatefirst.
After changing go.mod/go.sum, run task nix:sync — it resolves flake.nix's vendorHash and stages it. The pre-commit hook only verifies.
If a build fails with "no space left on device":
- Prune build cache first (safe):
docker buildx prune -af - If still insufficient, ask the user to stop old containers — never stop them yourself. Each pins a ~13 GB untagged image with almost no layer sharing, so 2–3 usually frees ~20 GB. Then
docker image prune.