github.com/muthuishere/wfnexus, formerly bug-fixer-platform), which is the
only consumer today with a real sandbox requirement and a real escape to test against.CreateBuiltinTools() builds bash, read, write, edit, apply_patch, grep,
glob closed over the host process — exec.Command and os.ReadFile directly. A host
cannot interpose. So the only way to sandbox a toolnexus agent is to disable the builtins
entirely and reimplement all ten, which is the hand-copying ADR 0012 exists to prevent,
and it discards our prompts, schemas and behaviour.
The evidence is not hypothetical. On a live wfnexus run an agent executed
cd <platform repo> && git log --all --grep=…, then git checkout -b, git stash,
git reset — in the platform's own repository. workdir sets only the INITIAL
directory; a cd in the command string leaves it, bash reports success, nothing errors.
Their ADR 0006 ships a cd/pushd/git -C guardrail and labels it honestly as a
guardrail and NOT a sandbox: an agent with a shell still has env, a symlink, a python
one-liner. No host can fix this from outside the tool.
A single uniform shape would quietly re-delegate a guarantee we spent the whole #86-#93 batch establishing. The split:
| tool | sandbox does | library does |
|---|---|---|
bash | decides — argv in, bytes + exit code out | nothing; does not interpret |
glob, grep, <skill_files> sample | enumerates only — returns the FULL candidate set (paths relative to the walk root, plus per-hit data for grep) | orders and truncates, in one place, in all seven ports |
read, write, edit, apply_patch | performs the point operation | error and result shaping |
Why the listing tools are not opaque. A25/A26 made those listings deterministic by
taking the ordering decision away from the filesystem: collect the whole candidate set,
sort by path relative to the walk root in plain code point, THEN truncate. If the sandbox
walks, sorts AND caps, every sandbox image becomes part of the conformance surface — two
images, or one image on two kernels with different readdir behaviour, ship different
<skill_files> contents for the same directory. That divergence is measured, not
theoretical: clojure's two hosts returned different file SETS for the same directory
under a cap-before-sort mutation (JVM {alpha-b.txt, mß.txt} vs cljgo
{a-dir/zz.txt, alpha/f.txt}).
The cap is what makes it matter. Without a cap that bites, sorting is cosmetic and either side could do it. With one, the sort chooses WHICH FILES THE MODEL SEES — and that is a library guarantee or it is nothing. That is the counter to a reviewer arguing for one uniform shape.
The failure mode if we get this wrong is the worst class available: it is invisible. "The model saw slightly different files today" does not read as a bug, it reads as the model being flaky, and no host would ever diagnose it.
The seam must distinguish:
Collapsing 1 and 2 breaks a consumer's retry policy (wfnexus ADR 0012): it cannot tell a flaky worker from a bad command and will retry things that can never succeed. This matches the rule #89 established in the #86-#93 batch: when the HOST has provably made a mistake, error to the host; when the MODEL has, hand the model something it can act on.
skill tool's not-found path returns a tool result listing what IS available.Also unresolved and worth a spike: what the seam does to read's offset/limit and the
<skill_files> sample, the two builtins explicitly ruled OUT of the batch's ordering work.
type BuiltinExec func(ctx context.Context, cmd BuiltinCommand) (BuiltinResult, error)
Nil ⇒ byte-identical to today (host exec). Set ⇒ every builtin routes through it.
Target use: podman run --rm --network=none --read-only --cap-drop=ALL --pid=private -v <run-worktree>:/workspace:rw -w /workspace <image>, under which the escape above dies
by construction — the platform's repo is not in the mount namespace, so there is nothing
to cd into.
ADR + spikes first, Go first, then all seven ports. Blocked on owner authorisation.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |