Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
fb12f8d
[CELL-329, CELL-330, CELL-331, CELL-332, CELL-333, CELL-334] Nix GC r…
DimmKirr Jul 25, 2026
63d760b
Swappable window manager support with IceWM Nord theme as default — `…
DimmKirr Jul 26, 2026
99956a5
[CELL-336, CELL-338, CELL-339] HiDPI scaling, D-Bus session bus, and …
DimmKirr Jul 26, 2026
3855db6
Conventional XDG_RUNTIME_DIR and WebKit sandbox disable for container…
DimmKirr Jul 28, 2026
4296d9b
Add $HOME/go/bin to PATH so go-install binaries (wails3) are found at…
DimmKirr Jul 29, 2026
0ef04ca
GTK4/WebKitGTK 6.0 build stack with dev headers, QEMU VM module, and …
DimmKirr Jul 29, 2026
4522de9
QEMU VM engine: full lifecycle for Windows guest provisioning, boot, …
DimmKirr Jul 29, 2026
e894703
Windows ISO tooling: UUPDump client, MCT catalog parser, ISO builder,…
DimmKirr Jul 29, 2026
844f8b2
`cell build/init/shell/rdp/vnc --engine=qemu` for Windows VM provisio…
DimmKirr Jul 29, 2026
fdf5b27
Fix desktop/GTK4 stack build aborting on an XKBgeom.h path collision
DimmKirr Jul 29, 2026
6a6546e
`task nix:validate` reports real syntax errors instead of failing eve…
DimmKirr Jul 29, 2026
a52a95e
[CELL-358] `sudo` works again in thin cells — setuid wrapper replaces…
DimmKirr Jul 29, 2026
26b79c4
Go toolchain pin moves into .mise.toml, and multi-GB Windows VM check…
DimmKirr Aug 3, 2026
720d15c
[CELL-331, CELL-372, CELL-375, CELL-383, CELL-391] `cell.kvm` gives Q…
DimmKirr Aug 3, 2026
e501275
Thin builds work against VM-backed and remote Docker daemons, and a r…
DimmKirr Aug 3, 2026
2480193
[CELL-334, CELL-379, CELL-390, CELL-391] `cell cleanup` reclaims the …
DimmKirr Aug 3, 2026
315739c
Global devcell config is now declarative — a home-manager module rend…
DimmKirr Aug 3, 2026
c686d3a
[CELL-362, CELL-383, CELL-392, CELL-398, CELL-402, CELL-405] Windows …
DimmKirr Aug 3, 2026
1c326a5
[CELL-372, CELL-375, CELL-377, CELL-378, CELL-383, CELL-390, CELL-391…
DimmKirr Aug 3, 2026
d614df3
`task nix:sync` is the one command after a dependency change, and the…
DimmKirr Aug 3, 2026
f2dbf6c
[CELL-331, CELL-332, CELL-334, CELL-391] The Windows guest's nix env …
DimmKirr Aug 3, 2026
cc24633
Android reverse engineering works on Apple Silicon, and Wine cells ca…
DimmKirr Aug 3, 2026
3bb3236
Document the libvirt engine — how to run Windows cells on the Mac hos…
DimmKirr Aug 3, 2026
5d9c02e
Project instructions become tracked and actually load — Claude Code w…
DimmKirr Aug 3, 2026
0ebfe17
The android module carries a full app reverse-engineering toolkit, so…
DimmKirr Aug 4, 2026
54fe4b2
[CELL-407, CELL-408, CELL-409] A cell can now replace Claude Code's b…
DimmKirr Aug 4, 2026
23b095a
[CELL-415] Each cell's desktop now shows its name in the wallpaper co…
DimmKirr Aug 4, 2026
17acf7f
[CELL-411, CELL-412, CELL-413, CELL-414] `cell telemetry on/off/statu…
DimmKirr Aug 4, 2026
356f14a
Two concurrent QEMU guests no longer blow through each other's deadli…
DimmKirr Aug 4, 2026
7ba8edc
TCG builds no longer drown in WerFault/Defender overhead, and the WSL…
DimmKirr Aug 4, 2026
1a7abc2
`task test:powershell:lint` catches broken .ps1 scripts before they r…
DimmKirr Aug 4, 2026
796e24b
In-cell scripts can now discover which cell they're running in via `D…
DimmKirr Aug 10, 2026
af8c544
The WSL guest's home-manager activation and nixos-rebuild no longer d…
DimmKirr Aug 10, 2026
786034d
home-manager activation no longer aborts on NixOS-WSL where sudo live…
DimmKirr Aug 10, 2026
72fadb8
The cell-ID wallpaper now renders correctly under bare icewm, at the …
DimmKirr Aug 10, 2026
9d0664d
`task debug:windows:start` boots the UTM debug disk under qemu-system…
DimmKirr Aug 10, 2026
6d8806b
[CELL-417] `cell codex` now receives container context and TOML appen…
DimmKirr Aug 11, 2026
bfbdc64
`TestClaude_CodeVersion` now builds a thin image per stack and persis…
DimmKirr Aug 11, 2026
08505fd
Test artifacts now land in `test/results/` on Lima/Colima hosts inste…
DimmKirr Aug 11, 2026
28be6ab
[CELL-418] `cell claude` (and every other agent) no longer silently f…
DimmKirr Aug 11, 2026
81c8b22
[CELL-428] macOS-mastered Windows ISOs are now firmware-bootable — El…
DimmKirr Aug 13, 2026
6326d77
[CELL-429] QEMU install and run commands now wire CDs on virtio-scsi …
DimmKirr Aug 13, 2026
06369d2
[CELL-429] WinPE now loads vioscsi via drvload before Setup scans for…
DimmKirr Aug 13, 2026
45007cd
[CELL-429] Integration tests for the full QEMU Windows boot pipeline …
DimmKirr Aug 13, 2026
8838585
[CELL-429] `cell init --engine=qemu` now detects corrupt or undersize…
DimmKirr Aug 13, 2026
1591c27
[CELL-429] `task test:windows:build` runs the full unattended Windows…
DimmKirr Aug 13, 2026
8dd0c39
[CELL-428, CELL-429] wimlib builds on all architectures, debug output…
DimmKirr Aug 13, 2026
7fe20b8
[CELL-418] Bootstrap now runs reliably after install, and network dia…
DimmKirr Aug 13, 2026
13097f7
[CELL-429] Serial log watcher now detects the Nth Windows Boot Manage…
DimmKirr Aug 13, 2026
c2bb364
[CELL-429] `cell build --engine=qemu` now runs DISM offline servicing…
DimmKirr Aug 13, 2026
1c5a0e0
[CELL-429] `cell build --engine=qemu` now runs DISM offline servicing…
DimmKirr Aug 14, 2026
4cf5752
[CELL-429] Bootstrap progress now streams live via virtio-serial, and…
DimmKirr Aug 14, 2026
e950327
[CELL-429] Hyper-V boot patches now enable the full service chain (vm…
DimmKirr Aug 14, 2026
719c3b9
[CELL-429] Registry patches can skip missing keys, and ReadDWord enab…
DimmKirr Aug 16, 2026
325fa18
[CELL-429] wimlib now builds on aarch64 by overriding the x86-only sy…
DimmKirr Aug 16, 2026
d49d185
[CELL-429] User volumes that duplicate standard mounts no longer caus…
DimmKirr Aug 16, 2026
bf04f0f
[CELL-429] Sparse qcow2 FAT volumes replace raw images for guest-host…
DimmKirr Aug 16, 2026
4ad1a43
[CELL-429] Post-DISM registry verification catches missing Hyper-V bo…
DimmKirr Aug 16, 2026
6d9cd8e
[CELL-429] WIM builder now injects WSL2 features, VirtIO drivers, and…
DimmKirr Aug 16, 2026
13e2bd7
[CELL-429] WinPE diagnostics now emit machine-parseable markers and r…
DimmKirr Aug 16, 2026
fac685f
[CELL-429] WinPE injection tests now retry firmware crashes, extract …
DimmKirr Aug 16, 2026
c2fe1cb
[CELL-429] End-to-end integration test drives the full WIM prep + Win…
DimmKirr Aug 16, 2026
613cf9c
docs(nix): note pkgs.formats.toml multiline limitation in appendPromp…
DimmKirr Aug 16, 2026
1abbd4d
docs(nix): note pkgs.formats.toml multiline limitation in appendPromp…
DimmKirr Aug 16, 2026
320be13
docs(nix): note pkgs.formats.toml multiline limitation in appendPromp…
DimmKirr Aug 16, 2026
416cd79
docs(nix): note pkgs.formats.toml multiline limitation in appendPromp…
DimmKirr Aug 16, 2026
c428e14
[CELL-445] [packages.nix] declarative nixpkgs in devcell.toml
DimmKirr Aug 18, 2026
4230dcd
[CELL-447] project-level flake.nix injection at container boot
DimmKirr Aug 18, 2026
dc083e4
fix(nix): remove x86-only wimlib from devShell
DimmKirr Aug 18, 2026
a056521
feat(claude): add --openrouter flag to route Claude Code through Open…
DimmKirr Aug 18, 2026
e21b88c
feat(ux): render ⚠ warning on nix store drift instead of ✓
DimmKirr Aug 20, 2026
78c3930
feat(nix): add graynet module (Tor SOCKS proxy) and scraping proxy ro…
DimmKirr Aug 20, 2026
f3b31bf
[CELL-453] WinPE bootstrap chain switches from cmd.exe to PowerShell …
DimmKirr Aug 21, 2026
88c04ed
[CELL-451] Add default_command, Docker resource limits, deterministic…
DimmKirr Aug 26, 2026
96bee90
[CELL-434, CELL-453, CELL-456] Add WinPE-hosted Hyper-V/VMP/WSL2 tran…
DimmKirr Aug 26, 2026
80f566d
Add cell start/stop, and stop cell shell from spinning up duplicate c…
DimmKirr Aug 26, 2026
93206c8
[CELL-461] Fix flake input breakage for consumers pinning feature/wip…
DimmKirr Aug 26, 2026
48921a7
chore(docker): update .dockerignore path for CONTINUE.md's move to .s…
DimmKirr Aug 26, 2026
e747ddc
Add task nix:build to verify the nix package actually builds
DimmKirr Aug 26, 2026
9a2c440
[CELL-465,CELL-468,CELL-469,CELL-470,CELL-471] Consumers can now reso…
DimmKirr Aug 27, 2026
4c9a14b
[CELL-426] Follow nixhome from its own devcell-sh/community-home repo…
DimmKirr Aug 27, 2026
f5243da
Fix `cell -c` (and other agent flags) dying at root-command parsing i…
DimmKirr Aug 27, 2026
1569d0c
Swap internal/nixstore for published go-nixoci module — no behavior c…
DimmKirr Aug 27, 2026
a26faa3
gofmt the repo
DimmKirr Aug 27, 2026
04b8a58
Move hmoptgen into cmd/ behind a build-ignore tag, like gendoc.go
DimmKirr Aug 27, 2026
8a8d353
[CELL-469] Drop internal/vm/qemu/winpe_stage.go — superseded by go-wi…
DimmKirr Aug 27, 2026
0ac6be1
Retire tools/ — hmoptgen joins cmd/, renderps1 moves to go-winkit
DimmKirr Aug 27, 2026
1538ab6
Restore go build ./... after retired unattend import broke internal/v…
DimmKirr Aug 27, 2026
e95e1c5
Docker builds now always run the thin nix-on-volume path, and nixhome…
DimmKirr Aug 27, 2026
2794524
Finish extracting nixhome into devcell-sh/community-home
DimmKirr Aug 27, 2026
8cb88d4
WinPE build logic now lives in the external go-winkit/winpe package, …
DimmKirr Aug 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
32 changes: 32 additions & 0 deletions .claude/rules/testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
paths:
- "test/**"
---

# Test buckets — short vs long

Integration tests in `test/` split by image-build cost.

**Short tests** assume a test image already exists (`DEVCELL_TEST_*_IMAGE`, or a local `devcell-user:*-thin`) and skip cleanly when it doesn't. They must NEVER call `buildLocalImage()`. The inner loop and per-PR CI run only these.

**Long tests** call `buildLocalImage(...)` to provision their own image (~5–10 min per stack) and MUST gate at the top:

```go
if testing.Short() { t.Skip("long: builds its own image") }
```

Nightly and release CI run these.

## Run modes

| Command | What runs | When |
|---|---|---|
| `go test -short ./test` | Short only | Inner loop, pre-commit, PR CI |
| `DEVCELL_TEST_THIN_IMAGE=<tag> go test ./test` | All, against a pinned tag, no build | PR CI after `docker pull` |
| `DEVCELL_TEST_BUILD_THIN=1 go test ./test` | `TestMain` builds ultimate-thin once, shared by all | Nightly, release |

## Rules

- Only `TestMain` and long tests may call `buildLocalImage`.
- Long tests start with the `testing.Short()` skip — no exceptions.
- Skip messages must name both the missing artifact and the command that supplies it, e.g. ``"set DEVCELL_TEST_DEV_IMAGE or run `cell build --stack dev --thin`"``.
16 changes: 16 additions & 0 deletions .claude/rules/vocabulary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Vocabulary

The runtime model has five entities. Use these words consistently in code, comments, error messages, log output, docs, and prose — a rename in one layer without the others is what made the old naming ambiguous.

- **cell** — a named, persistent identity, and a boundary: one shared `$HOME` (`~/.devcell/<cellName>/`), one network, one secrets scope. May host many projects. May be running or stopped. Defaults to `main`; override with `DEVCELL_CELL_NAME`, or inherit from `TMUX_SESSION_NAME`.
- **project** — a host directory with code, mounted into a container.
- **container** — the running docker instance for one (cell, project) pair. Ephemeral.
- **stack** — the image variant a container is built from.
- **module** — a toggleable Nix capability composed into a stack.

## Retired words

Do not use these as devcell-layer terms:

- ~~**session**~~ — tmux owns this word.
- ~~**workspace**~~ — survives only in `internal/serve/` for the MS-TSWP RDP protocol, where it is the protocol's own term. `WorkspaceResource` is still pending a rename to `Cell`.
9 changes: 9 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,12 @@ web/.context
docs/
test/js/
test/results/
.workspaces/
.worktrees/
.gocache/
.gomodcache/
.venv/
.vagrant/
.playwright-mcp/
Vagrantfile.local
.scratch/CONTINUE.md
18 changes: 7 additions & 11 deletions .github/workflows/build.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ jobs:
- name: Hydrate /nix volume from GHCR cache
if: inputs.skip_nix_cache != true
run: |
HASH=${{ hashFiles('nixhome/**') }}
HASH=${{ github.sha }}
./bin/cell nix-store pull \
--image "${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:nix-cache-${{ matrix.arch }}-${HASH}" \
--fallback "${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:nix-cache-${{ matrix.arch }}-latest" \
Expand All @@ -125,17 +125,16 @@ jobs:

# Build both stacks sequentially in the same job so the nix-store
# volume accumulates derivations from both — single tar dump at job
# end carries everything needed by docker-test. `cell build --thin`
# end carries everything needed by docker-test. `cell build`
# reuses store paths across the two invocations (nix is
# content-addressed), so ultimate after base is incremental.
- name: Build thin image (base stack)
env:
DEVCELL_NIX_VOLUME: devcell-nix-store-${{ matrix.arch }}
DEVCELL_NIXHOME_PATH: ${{ github.workspace }}/nixhome
DEVCELL_NIX_MAX_JOBS: "4"
run: |
BASE_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:v0.0.0-${{ matrix.arch }}-base"
./bin/cell build --thin --stack base --image "$BASE_TAG" --debug
./bin/cell build --stack base --image "$BASE_TAG" --debug
echo "BASE_TAG=$BASE_TAG" >> "$GITHUB_ENV"

# Interim publish: lock in the base-stack derivations as a usable
Expand All @@ -149,19 +148,18 @@ jobs:
timeout-minutes: 45
env:
ARCH: ${{ matrix.arch }}
HASH: ${{ hashFiles('nixhome/**') }}
HASH: ${{ github.sha }}
STAGE: post-base
DEVCELL_NIX_PUSH_DEBUG: "1"
run: task nix-cache:publish

- name: Build thin image (ultimate stack)
env:
DEVCELL_NIX_VOLUME: devcell-nix-store-${{ matrix.arch }}
DEVCELL_NIXHOME_PATH: ${{ github.workspace }}/nixhome
DEVCELL_NIX_MAX_JOBS: "4"
run: |
ULT_TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:v0.0.0-${{ matrix.arch }}-ultimate"
./bin/cell build --thin --stack ultimate --image "$ULT_TAG" --debug
./bin/cell build --stack ultimate --image "$ULT_TAG" --debug
echo "ULT_TAG=$ULT_TAG" >> "$GITHUB_ENV"

- name: Push to GHCR (both stacks)
Expand All @@ -177,7 +175,7 @@ jobs:
timeout-minutes: 120
env:
ARCH: ${{ matrix.arch }}
HASH: ${{ hashFiles('nixhome/**') }}
HASH: ${{ github.sha }}
STAGE: post-ultimate
DEVCELL_NIX_PUSH_DEBUG: "1"
run: task nix-cache:publish
Expand Down Expand Up @@ -228,7 +226,7 @@ jobs:

- name: Hydrate /nix volume from GHCR cache
run: |
HASH=${{ hashFiles('nixhome/**') }}
HASH=${{ github.sha }}
./bin/cell nix-store pull \
--image "${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:nix-cache-${{ matrix.arch }}-${HASH}" \
--fallback "${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:nix-cache-${{ matrix.arch }}-latest" \
Expand Down Expand Up @@ -384,8 +382,6 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}

- name: Run cell claude --version (full pipeline)
env:
DEVCELL_NIXHOME_PATH: ${{ github.workspace }}/nixhome
run: |
# Simulate a new user in a fresh project dir
mkdir -p /tmp/e2e-project && cd /tmp/e2e-project
Expand Down
13 changes: 5 additions & 8 deletions .github/workflows/build.release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,12 @@ jobs:

# Hydrate /nix volume from the GHCR cache image populated by
# build.dev.yml (same `nix-cache-${arch}-*` tag scheme). Release
# builds reuse the same nixhome closure as dev builds for any
# given `hashFiles('nixhome/**')`, so the cache is interchangeable.
# builds reuse the same nixhome closure as dev builds, so the
# cache is interchangeable.
- name: Hydrate /nix volume from GHCR cache
if: inputs.skip_nix_cache != true
run: |
HASH=${{ hashFiles('nixhome/**') }}
HASH=${{ github.sha }}
./bin/cell nix-store pull \
--image "${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:nix-cache-${{ matrix.arch }}-${HASH}" \
--fallback "${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:nix-cache-${{ matrix.arch }}-latest" \
Expand All @@ -103,10 +103,9 @@ jobs:
id: build
env:
DEVCELL_NIX_VOLUME: devcell-nix-store-${{ matrix.arch }}
DEVCELL_NIXHOME_PATH: ${{ github.workspace }}/nixhome
run: |
TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:${{ env.VERSION }}-${{ matrix.arch }}"
./bin/cell build --thin --image "$TAG" --debug
./bin/cell build --image "$TAG" --debug
echo "tag=$TAG" >> "$GITHUB_OUTPUT"

- name: Push to GHCR
Expand All @@ -120,7 +119,7 @@ jobs:
timeout-minutes: 45
env:
ARCH: ${{ matrix.arch }}
HASH: ${{ hashFiles('nixhome/**') }}
HASH: ${{ github.sha }}
STAGE: release
run: task nix-cache:publish

Expand Down Expand Up @@ -236,8 +235,6 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}

- name: Run cell claude --version (full pipeline)
env:
DEVCELL_NIXHOME_PATH: ${{ github.workspace }}/nixhome
run: |
# Simulate a new user in a fresh project dir
mkdir -p /tmp/e2e-project && cd /tmp/e2e-project
Expand Down
33 changes: 29 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
.workspaces
.worktrees
nixhome/user.nix
.venv
.env.devcell
*.egg-info
Expand All @@ -18,20 +17,46 @@ web/src/content/cell/
bin/
.playwright-mcp
test/testdata/**/nixhome
test/testdata/windows-arm64.*
# Versioned ssh-able base images (13+ GB each) the dev-env test builds on.
test/testdata/windows-sshable-*.qcow
# WSL-ready checkpoints (drivers + share + WSL engine baked in).
test/testdata/windows-wsl-*.qcow
# Nix-provisioned checkpoints (WSL checkpoint + home-manager activated).
test/testdata/windows-nix-*.qcow
# UTM bundle: the debug VM's full disk + EFI vars + config (~24 GB).
test/testdata/Windows.utm/
# Kernel-bootable firmware shared with the host for task debug:windows:start.
test/testdata/QEMU_EFI.kernel.fd
# Stable $HOME for the CLI-driven Windows build test: holds the cached ISOs and
# the installed template, so a rerun is a boot rather than a 2h47m reinstall.
test/testdata/cellhome/
test/results
test/results_archive
.devcell
.devcell.toml
.gocache
.gomodcache
docs/
.nixhome-tmp/
.rendered-ps1/

# macOS
.DS_Store

# Personal dev config / scratch (not project state)
.claude/
# Personal dev config / scratch (not project state).
# `.claude/*` (not `.claude/`) so the shared subdirs below can be re-included —
# git cannot un-ignore a path inside an excluded directory.
.claude/*
!.claude/rules/
.context/
.scratch/
.envrc
.idea/
CLAUDE.md
CLAUDE.local.md
# task debug:windows runtime state
.tmp/

# local multi-repo workspace
go.work
go.work.sum
4 changes: 3 additions & 1 deletion .mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
# git hooks land automatically.
#
# Nix-native contributors get the same behavior from the shellHook
# in flake.nix; `.tool-versions` handles Go pinning for asdf users.
# in flake.nix.
[tools]
# Keep in sync with the `go` directive in go.mod.
go = "1.26"
pre-commit = "4.0.1"

[hooks]
Expand Down
15 changes: 5 additions & 10 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,13 @@ repos:

- repo: local
hooks:
# Keep flake.nix `vendorHash` in sync with go.mod / go.sum.
# Fires only when the Go module surface changes; delegates to
# `task nix:update-vendor-hash`, which builds .#cell with a fake
# hash, extracts the real hash from the mismatch error, and
# rewrites flake.nix in place. If the hash actually changed,
# pre-commit aborts the commit so the user reviews the diff and
# re-stages flake.nix — that guarantees any tag pointing at the
# commit has the correct hash baked in.
# Warn (non-blocking) when go.mod/go.sum change and vendorHash
# may be stale. Run `task nix:sync` to fix before pushing.
- id: nix-vendor-hash
name: Sync flake.nix vendorHash with go.mod/go.sum
entry: task nix:update-vendor-hash
name: Check flake.nix vendorHash matches go.mod/go.sum
entry: task nix:check-vendor-hash
language: system
files: ^(go\.mod|go\.sum)$
pass_filenames: false
require_serial: true
verbose: true
1 change: 0 additions & 1 deletion .tool-versions

This file was deleted.

59 changes: 59 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# devcell — Project Instructions

## Terminology

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.

## Git Policy

- Do NOT create commits automatically. Always ask the user to commit.
- Do NOT push to remote unless the user explicitly asks.

## TDD

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 environment layout

- Nix is owned by the `devcell` user, 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 every `home-manager switch`.
- The entrypoint copies `/opt/devcell/` dotfiles to `/home/$HOST_USER/` with `sed "s|/opt/devcell|$HOME|g"` to redirect write paths.
- Use `ln -sfT` (not `ln -sf`) when replacing a symlink-to-directory; `-T` prevents creating the link *inside* the target.
- `ENV USER=devcell` is required in the nix stage — `nix.sh` checks `[ -n "$USER" ]` and silently no-ops if empty.
- `$HOME/.config/nix/nix.conf` must carry `experimental-features = nix-command flakes` at BUILD time.

## Architecture detection in Dockerfiles

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 module edits

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 `''''`
- `$VAR` without braces passes through as-is

## Go module and generated-docs hygiene

The CI **Deploy Site** workflow compiles all `cmd/*.go` together with `cmd/gendoc.go`. Three things must hold or `go build` exits 1:

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 missing `go.sum` entry only surfaces there.
2. Build-time-only tooling deps must stay anchored in `cmd/tools.go` (`//go:build tools`). `cmd/gendoc.go` is `//go:build ignore`, so `go mod tidy` can't see its `cobra/doc` import and would prune the transitive deps. Anchor any other build-ignored tool's deps there too.
3. `docs/` is gitignored (swagger output) but `cmd/serve.go` imports it, so any workflow compiling `serve.go` must run `task swagger:generate` first.

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.

## Disk space

If a build fails with "no space left on device":

1. Prune build cache first (safe): `docker buildx prune -af`
2. 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`.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
Loading
Loading