feat(nix): add a dev shell and a checks output (task 0039) #48
Reference in New Issue
Block a user
Delete Branch "task-0039-dev-shell-and-checks"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implements
.claude/tasks/0039-dev-shell-and-checks.md.What was built
Two further flake outputs, so
nix developgives a declarative answer to "what do I need to work on this" andnix flake checkis not a silent no-op.devShells.defaultcarries Node,git,tea, andcurl— enough for the build, the fast tier, the live end-to-end tier, and the benchmark harness's runner.checksaliases the package, sonix flake checkbuilds it and thereby runs both its verification phases (checkPhase,installCheckPhase).forAllSystemsnow hands each output the system name alongside the package set, since the shell and the checks reach back intoself.packages.Verified by running it:
nix developyields Node 24.18.0 from the same store path as the package's;npm ci,npm run build,npm run test(410 passing) andnpm run bench:runall work inside the shell;nix flake checkpasses, and fails with exit 1 when a deliberately failing assertion is added to the fast tier.Deviations
The single Node reference became a
passthru. The first cut readnodejsback off the derivation, which works only as a side effect ofbuildNpmPackagesurfacing its arguments — nothing marked it load-bearing, so moving thatinheritwould have broken the shell silently at a distance.package.nixnow declarespassthru = { inherit nodejs; }with a comment saying what depends on it.passthrudoes not enter the derivation, so the store path is unchanged (verified: identical drv hash before and after).curlwas added to the shell. The benchmark'sraw-apiarm shells out to it.The shell deliberately omits
gitea-axiitself.provisionArmBinresolves each arm's binary by name offPATH, and thegitea-axiarm's must be the locally builtdist/main.jsso a run measures the working tree rather than whatever the flake last packaged — supplying the packaged one would silently substitute the wrong artifact. Exposing the built one was also rejected:tscsets no executable bit ondist/main.js, so ashellHookwould have had tochmod +xthe build output on every shell entry. Both the flake and CLAUDE.md now state the boundary rather than implying the shell covers it.CLAUDE.md's toolchain gotcha was stale by construction — it read "the repository has no dev shell yet (task 0039 adds one)". Rewritten to point at
nix develop --command.Review
Risk
Overall: MEDIUM
forAllSystems's signature change is internal toflake.nixand every call site is updated in the same diff.git revertrestores the prior state with no migration or published-schema residue.checksoutput is itself the test wiring, but nothing asserts the dev shell contains the right tools.teaappears only as a shell package, not as a credential path change.packages.defaultderivation is untouched.Unaddressed findings
Standards — nothing asserts the dev shell's contents. No test guards that the shell keeps carrying Node,
git,tea, andcurl; a regression would surface only when someone's workflow breaks. Left alone: a check derivation instantiating the shell to assert itsPATHis exactly the per-stage granularity the task rules out, and the shell is dev-only.Spec —
nix flake checkis a no-op when the package is already built. Becausechecksaliases the package, a cached store path means nothing rebuilds. This is inherent to aliasing rather than a defect — it is the same caching that makes any Nix check cheap, and the failure path was verified directly by forcing a rebuild with a failing test.