Files
improvise/context/cloud-agents.md
T
Claude 8cf8f01f12 feat(scripts): make nix develop work in cloud sessions without touching the flake
Nix's `github:` fetcher wants a tarball from
github.com/<owner>/<repo>/archive/<rev>.tar.gz, and that endpoint is not
served in a cloud session. Three things that look like fixes are not:

  - Raising the environment's network access to Full, or allowlisting
    codeload.github.com, changes nothing: GitHub traffic bypasses the
    domain allowlist entirely and goes through a proxy that scopes API
    and release-asset requests to repositories attached to the session.
    An input from any other owner gets a 403.
  - Attaching the dependency repos is refused -- cross-tier adds are not
    supported once the session has sources from another owner.
  - Nix has no setting that makes `github:` inputs use the git transport.

The lock's own integrity machinery is what works. flake.lock records a
narHash per input, and Nix looks a source up in the store and its
substituters before fetching anything. The git and tarball fetchers
produce byte-identical trees for a revision, so prefetching the locked
rev over git+https lands the input at exactly the store path the lock
names:

  lock narHash:  sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=
  git prefetch:  sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=

scripts/nix-seed-flake-inputs.sh does that for every locked input. It
reads flake.lock and never writes it. Nix still verifies each tree
against the recorded hash, so this satisfies the lock rather than working
around it -- a mismatch fails loudly. Rewriting the lock's nodes to `git`
type and carrying the old narHash across would have been the opposite:
that hash is the claim the lock exists to make.

Verified from a fresh clone with a cleared git cache: 15/15 inputs
seeded, then `nix develop` gives rustc 1.98.1, cargo and crate2nix, with
flake.nix and flake.lock unmodified.

Not wired into the session-start hook: it costs minutes and several GB
once the devShell builds, and most work only needs the preinstalled cargo
toolchain.

Corrects three places that claimed nix could not work here --
context/cloud-agents.md, the design-review skill's kb.pl check, and the
hook's environment notes -- all written before the git transport was
tested. The doc also records the two alternatives, a binary-cache
substituter and a self-hosted environment.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DyHK7L8S1XDX3EihTnSKaX
2026-09-19 06:52:17 +00:00

6.1 KiB

Cloud Agents (LLM Reference)

Environment notes for agents running in Claude Code on the web. Local checkouts are unaffected — the session hook exits early unless CLAUDE_CODE_REMOTE=true.

.claude/hooks/session-start.sh handles the setup automatically. This doc is the "why", and the record of what bites when it doesn't run.


The issue tracker is not issues.jsonl

.beads/issues.jsonl is a stale export. It is a viewer/interchange artifact, not the source of truth — .beads/README.md says as much ("Cross-machine sync and backups use Dolt remotes/backups, not JSONL import/export"). The real tracker is the Dolt database published to refs/dolt/data on the git remote.

The gap is not cosmetic. A session that hydrated from the JSONL saw 121 issues against the remote's 168, with every P1 bug showing OPEN though all four were fixed and closed. Two beads were implemented from scratch that the remote had recorded as closed weeks earlier, and which turned out to be already implemented on an unmerged branch.

The hook syncs the database from the remote at session start. If it did not run, or reported a failure:

bd list --all | wc -l     # 121 means you are on the stale export

Verify any bead against the code before starting it. git log --oneline --all | grep -oE 'improvise-[a-z0-9]{3}' names the ones already referenced by a commit, and git log origin/main is worth a look for work that landed without a bead reference.


bd init is destructive here

A bare bd init in this repo rewrites AGENTS.md, CLAUDE.md and .claude/settings.json, installs Cursor/Codex integration files, and commits all of it. It also rewrites sync.remote in .beads/config.yaml.

Always pass the opt-outs:

bd init --non-interactive --skip-agents --skip-hooks -q --remote git+<origin>

Even then it commits a small .gitignore change, so the hook runs it in a throwaway directory and copies only .beads/embeddeddolt back. Do the same if you need to re-sync by hand; never run it in the working tree.


What does not work in this environment

Thing Status Use instead
nix develop unseeded Fails: flake inputs are GitHub tarballs, which are not served here Run scripts/nix-seed-flake-inputs.sh first — see below. Or just use the preinstalled cargo/rustc, the same stable toolchain the flake pins
bd dolt push Fails: 403 outbound Nothing; bead status changes stay local. Say so rather than implying the tracker was updated
git+ssh:// Dolt remote in config.yaml No ssh client in the container git+https://<origin>, which the hook passes explicitly
git push, GitHub API Work normally —

Nix itself installs and runs fine (no init system, so the daemon needs starting by hand). Reads of public GitHub repos over the git protocol work — it is specifically the /archive/*.tar.gz and codeload endpoints that are refused.

Making nix develop work

scripts/nix-seed-flake-inputs.sh makes the devShell work without touching flake.nix or flake.lock. Verified end to end: rustc 1.98.1, cargo, crate2nix in the shell, git status clean.

Why it is needed, and why the obvious fixes are not available:

  • Nix's github: fetcher wants a tarball. GitHub traffic bypasses the environment's domain allowlist entirely and goes through a proxy that scopes API and release-asset requests to repositories attached to the session. A flake input from any other owner gets a 403. Raising network access to Full, or allowlisting codeload.github.com, therefore does nothing.
  • Attaching the dependency repos is refused: cross-tier adds are not supported in v1 once the session already has sources from another owner.
  • Nix has no setting that makes github: inputs use the git transport.

What does work is the lock's own integrity machinery. flake.lock records a narHash per input, and Nix looks a source up in the store and its substituters before fetching. The git and tarball fetchers produce byte-identical trees for a revision, so a git+https prefetch at the locked rev lands the input at exactly the store path the lock names:

lock narHash:  sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=
git prefetch:  sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=

The script does that for every locked input. Nix still verifies each tree against the recorded hash, so this satisfies the lock rather than working around it — a mismatch fails loudly. Do not instead rewrite the lock's nodes to git type: carrying the old narHash onto a different fetcher is exactly the claim the lock exists to make.

It is not in the session-start hook because it is expensive (minutes, plus several GB of store once the devShell builds) and most work does not need the devShell. Run it when you do.

Two alternatives if per-container seeding is unattractive: push the inputs to a binary cache with nix flake archive and point sessions at it with NIX_CONFIG/extra-substituters plus a Custom allowlist entry for the cache host (not GitHub, so the allowlist does apply); or use a self-hosted environment, where egress leaves through your own network boundary.

cache.nixos.org is reachable, so the build itself substitutes normally.


Sharp edges in bd

  • bd dolt push <remote> ignores its argument and pushes to origin. Pointing it at a throwaway remote does not keep the real one out of it; only the 403 does. Treat the named form as unsafe.
  • bd dolt pull into a divergent local database fails with local changes would be stomped by merge. Re-sync from the remote instead of merging.
  • bd warns if .beads is not mode 0700.

Working conventions

  • One bead per pull request. Stacking a second bead onto an open PR has been rejected in review.
  • A merged PR cannot carry follow-up work: restart from the latest default branch and open a new PR.
  • Prefer merges to rebasing (AGENTS.md), including when bringing the default branch into a working branch.
  • Keep context/repo-map.md and context/design-principles.md current — they are the two docs agents are expected to maintain by hand.