Skip to content

bd lives in a maintainer shell, so a contributor's dev shell is unchanged - #215

Merged
GraemeF merged 1 commit into
mainfrom
comms-v22p
Aug 3, 2026
Merged

GraemeF merged 1 commit into
mainfrom
comms-v22p

Conversation

@GraemeF

@GraemeF GraemeF commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

The maintainers' bd tracker is not part of this repository — external contributors file GitHub issues instead, and AGENTS.md says so. But the client still has to be resolvable at a known version: an older bd against a newer tracker schema exits 0 and corrupts, so "whatever bd is on PATH" is not a safe answer for the people who do use it.

The obvious fix — put bd in the default dev shell — charges every external contributor a Go 1.26 toolchain for a tool they have no use for, and starts documenting a tracker the repo says it does not have.

What this does instead

devShells.maintainer is the default shell plus bd. The beads flake input is reachable only from that output.

Nix fetches flake inputs per output, so a contributor entering nix develop — or CI entering nix develop .#ci, which is all ci.yml and release.yml ever do — never fetches, evaluates or builds it.

Measured rather than assumed. Poisoning the beads input's narHash in flake.lock and clearing the eval cache:

poisoned beads hash
nix develop .#maintainer exit 102, NAR hash mismatch — the control, confirming the poison is live
nix develop .#default exit 0 — input untouched
nix develop .#ci exit 0 — input untouched

nix flake check does evaluate every devShell and so does reach the input; nothing in CI runs it.

No inputs.nixpkgs.follows — beads needs Go 1.26 and this flake's nixpkgs carries 1.25, so it keeps its own. This repo's own nixpkgs pin is byte-identical before and after (b3da656); the diff only renames its lock node.

The untracked half

Providing the shell is not the same as selecting it, and selecting it is the part that must stay out of a public repo. .envrc gains one line:

source_env_if_exists .envrc.local

Inert when the file is absent, so contributors are unaffected — and a standard direnv idiom for local overrides if they want one. A maintainer creates an untracked .envrc.local containing use flake .#maintainer; .gitignore now covers it, so nobody commits it by accident and nobody carries a permanently dirty tree.

End to end, with a negative control:

# no .envrc.local
$ direnv exec . bash -c "which bd; bd --version"
/etc/profiles/per-user/graeme/bin/bd
bd version 1.0.4 (dev)

# with .envrc.local
$ direnv exec . bash -c "which bd; bd --version"
/nix/store/…-beads-1.1.2/bin/bd
bd version 1.1.2 (dev)

The tracker stays out of the repository. Only the seam for reaching it is in.

Why it is no longer a draft

It was held because this is one of two ways to solve the problem and the other is fleet-side. That call has since been made: the fleet-side option is scheduled last, after all four tracker cutovers, so it is not arriving in time to make this unnecessary. This is the answer for now.

One honest limitation

.envrc.local is per-checkout and untracked, which is the point — but it means a git worktree does not inherit it. A fresh worktree gets the tracked .envrc and no local override, so it falls back to ambient bd.

That is not theoretical. Measured on this machine, 2026-08-03: an unpinned worktree ran bd 1.0.4 against a v53 tracker and got correct data, exit 0, no forward-drift warning and nothing on stderr. --ignore-schema-skew reads like a promise that something refuses without it; nothing did.

So the seam closes the interactive-shell hole and does not close the worktree hole. Whoever cuts a worktree has to carry the override into it. Fixing that properly means either putting bd in the default shell (the cost this PR exists to avoid) or a direnv source_up-style walk, which double-loads use flake. Neither is worth it here; naming it is.

https://claude.ai/code/session_01891GkCj1Eb7YZLBJiBMT7T

…nged (comms-v22p)

The maintainers' bd tracker is not part of this repository, but its client
has to be resolvable at a known version — an older bd against a newer tracker
schema exits 0 and corrupts, so "whatever bd is on PATH" is not a safe answer.

Putting bd in the default dev shell would charge every external contributor a
Go toolchain they have no use for. Nix fetches flake inputs per output, so a
`maintainer` shell keeps the cost where the benefit is: with the beads input
reachable only from that shell, `nix develop` and `nix develop .#ci` never
fetch, evaluate or build it. Verified by poisoning the input's narHash in
flake.lock — `.#default` and `.#ci` still evaluate, `.#maintainer` fails.

Selecting the shell is the untracked half: `.envrc` gains a
`source_env_if_exists .envrc.local`, inert when the file is absent, and a
maintainer puts `use flake .#maintainer` in that file. The tracker stays out
of the repository; only the seam for reaching it is in.

Claude-Session: https://claude.ai/code/session_011WxL3mg6KnFnw8jHp31jsU
@GraemeF
GraemeF marked this pull request as ready for review August 3, 2026 08:03
@GraemeF
GraemeF merged commit 05b96d8 into main Aug 3, 2026
2 checks passed
@GraemeF
GraemeF deleted the comms-v22p branch August 3, 2026 09:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant