README: drop the PATH note — the fix belongs in the host config, not the docs
Operator: rather than documenting the workaround, notplants-nix should put the system profile on the agent PATH. It already does (two orchestrator units export it); the gap is that the change is committed and not yet deployed. The /proc rewrite in agents.py stands on its own — harness code should not depend on an external tool for something the kernel exposes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V3LdmEL7CvCYTNpoBq1kce
This commit is contained in:
@@ -480,55 +480,6 @@ The agent CLIs themselves (`claude`, `opencode`) are **external, non-Nix tools**
|
||||
per their own docs and make sure they are on `PATH` before launching live agents. The devShell
|
||||
documents this in its banner.
|
||||
|
||||
### PATH on a NixOS host: why a tool that IS installed says "command not found"
|
||||
|
||||
**An agent's `PATH` does not include the system profile.** On `notplants-orchestrator` the agent
|
||||
shell gets a pinned list of individual store paths (bash, git, python, coreutils, findutils, grep,
|
||||
sed, systemd, tmux, openssh, net-tools) and **not** `/run/current-system/sw/bin`. Everything the
|
||||
host declares in `environment.systemPackages` therefore exists and is unreachable.
|
||||
|
||||
Measured 2026-08-21: `ps`, `pgrep`, `free`, `cmp`, `awk`, `curl`, `diff`, `strings`, `nm`, `getent`
|
||||
and `ping` were ALL installed (notplants-nix `hosts/notplants-orchestrator/configuration.nix`,
|
||||
"give agents a real toolbox") and all reported `command not found`.
|
||||
|
||||
**Why this is worse than an inconvenience.** A missing binary run through `shell=True` returns
|
||||
**rc=127 with empty stdout**, and empty stdout is indistinguishable from a real answer of "none":
|
||||
|
||||
- `agents.py` shelled out to `pgrep -P` to find a pane's child processes. With `pgrep` unreachable
|
||||
it returned *no children*, so `_build_running` was **always False** and the stall detector could
|
||||
reboot an agent in the middle of a build. It shipped that way and no test caught it, because the
|
||||
unit tests **mocked `pgrep`** — the fake stood in for the broken dependency.
|
||||
- The same session read an empty `ps` as "0 agent processes running", and an empty `strings` as
|
||||
proof that a binary had its features stripped.
|
||||
|
||||
**Check before concluding a tool is absent:**
|
||||
|
||||
```bash
|
||||
ls /run/current-system/sw/bin/<tool> # installed but unreachable?
|
||||
command -v <tool> # reachable?
|
||||
```
|
||||
|
||||
**Put the system profile on PATH** (appended, so the sandbox's pinned store paths keep priority):
|
||||
|
||||
```bash
|
||||
case ":$PATH:" in *":/run/current-system/sw/bin:"*) ;;
|
||||
*) export PATH="$PATH:/run/current-system/sw/bin" ;; esac
|
||||
```
|
||||
|
||||
For something genuinely not installed, fetch it without changing the host:
|
||||
|
||||
```bash
|
||||
nix shell nixpkgs#tcpdump -c tcpdump ... # one command, nothing persisted
|
||||
nix run nixpkgs#git-filter-repo -- --help
|
||||
```
|
||||
|
||||
`nix` itself may also be off `PATH`; it lives at `/nix/var/nix/profiles/default/bin/nix`.
|
||||
|
||||
**Rule for harness code: do not shell out for something the kernel already exposes.** `agents.py`
|
||||
now reads `/proc/<pid>/stat` and `/proc/<pid>/comm` directly instead of calling `pgrep` and `ps`.
|
||||
That has no PATH dependency and cannot fail silently in the direction that matters. If you must
|
||||
call an external tool, check the return code — never treat empty output as an answer.
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
Reference in New Issue
Block a user