ADR 007: Execution modes and structured diagnostics
Status
Accepted — 2026-10-17. Revised 2026-10-18 with passthrough output.
Context
Ordinary tooling expects stdout/stderr, command-line switches and exit codes. Agents need bounded machine-readable results with error locations and the effective policy. Earlier revisions tied the JSON result to the sandbox, which forced a choice between a usable terminal tool and an isolated one.
Decision
Output format and isolation are independent. By default the sandboxed task's
stdin, stdout and stderr are the user's; exceptions print Python's traceback,
followed by pynox: <hint> when a permission or memory hint applies; launcher,
timeout and signal failures print one pynox: line. Exit codes are Python's,
plus 124 for timeout, 125 for launcher or setup failure and 2 for invalid
Pynox flags.
--json captures both streams, bounded by --max-output, and prints one JSON
result: exit, stdout, stderr, truncation flags, duration, runtime and the
effective policy including implicit grants, and an error with type, message,
file, line and hint. The pool returns the same shape plus request IDs.
--no-sandbox hands the command line to CPython unchanged. Pynox's own flags
are long options CPython does not use, so they can precede Python's.
In the sandbox the program is a script, -c or -m; isolation and buffering
options are accepted as no-ops, other interpreter options are refused with a
pointer to --no-sandbox. pynox doctor reports runtime metadata, probes
Landlock, prctl and pidfd_open without changing its own process, and
returns sandbox_supported with sandbox_unavailable_reasons, separate from
runtime_supported, so a blocked environment is diagnosed before a task fails.
The pool reports on itself as well as on tasks: a stats request returns
counters by outcome and error type, and --log=json emits one stderr line per
job with identifiers, outcome and timing. Neither carries captured output,
which may hold secrets; logging is off by default so embedding applications
do not inherit noise on their stderr.
Alternatives considered
- JSON on every invocation: unusable as a terminal tool.
- Tracebacks only: a model cannot act on them reliably.
- Support every CPython option inside the sandbox: each needs its own isolated-configuration mapping; the three program forms cover scripts, tests and tools.
Consequences
Passthrough tracebacks omit the launcher's frame but otherwise match Python.
Partial output before a RecursionError differs by that frame. The diagnostic
channel is written by the task and is not an audit log.