Run a shell command with the Workspace as the working directory.
Before crossing into host process execution, execute/2 acquires a bounded
host-command lease from Pixir.Tools.CommandBoundary (ADR 0027). This keeps OS
process fanout separate from BEAM-local Subagent/Workflow fanout.
Runs via a Port so a hung command can be killed on timeout. A portable
Perl wrapper makes the spawned OS pid a process-group leader before it execs
bash; Pixir then signals the whole group with SIGTERM and escalates it to
SIGKILL after a short grace period before closing the port. An unlinked reaper
monitors the collecting process and performs the same group cleanup if that
process is brutally killed before it can return. A final direct-child sweep is
belt-and-suspenders cleanup. Pending port messages are drained after close so
repeated timeouts do not dirty the caller mailbox. Deliberately double-forked
daemons that leave the process group are a residual out of scope.
The timeout is an open knob: an agent-supplied timeout_ms replaces
context.bash_timeout_ms or config :pixir, :bash_timeout_ms (default 120s),
but is always capped by bash_timeout_max_ms (default 600s). Host-command
concurrency and queueing use host_commands config.
v0.1 safety confines the cwd and rejects shell tokens that visibly resolve outside
the workspace — parent-directory references, absolute paths, home/env-home paths, and
existing symlink-prefix escapes — before crossing the host boundary. Only RHS values
of leading POSIX environment assignments before a simple command are ignored; literal
path arguments, redirection targets, and non-leading NAME=VALUE values are still
checked. The accepted residual vector VAR=/outside cmd $VAR can expand at runtime,
because this is a conservative tripwire, not a full shell parser or sandbox. The
permission gate (ADR 0006) is still the higher-level guard: under :ask, non-safe
commands prompt; under :read_only they are refused.