Short answer: treat EXIT as process evidence, not an agent verdict
AgentSight v1.0.35 listens to Linux sched_process_exec and sched_process_exit, subject to its tracing scope and output filters. The userspace tracer emits JSON EXEC and EXIT rows with process identifiers, a command name and event-specific fields. A process can exit normally while its work is semantically wrong; another can be terminated by a signal. Neither a zero high-byte value nor an absent row proves task success.
This is a source-level analysis pinned to commit c1574366122a90c451bca6a84713e28813ab95e2, not an executed AgentSight capture, a universal shell-status decoder, or a measured reliability result. When deciding whether a tool succeeded, retain its own command result, relevant output and task-level checks alongside any process trace.
Why an EXIT zero may not be a normal exit(0)
The BPF exit handler assigns the following expression to the recorded field:
e->exit_code = (BPF_CORE_READ(task, exit_code) >> 8) & 0xff;Linux wait status distinguishes ordinary termination from signal termination with separate predicates, such as WIFEXITED and WIFSIGNALED. AgentSight's JSON exit_code stores neither predicate nor the original status word. For typical wait-style encodings, 0x0700 becomes 7, while a signal-oriented status whose low byte is 0x0f becomes 0 after the same shift and mask. This is a bit-extraction example, not evidence that a particular recorded PID was terminated by SIGTERM.
An investigator therefore cannot recover WIFSIGNALED orWTERMSIG from the displayed exit_code alone. A shell's familiar 128 + signal convention is also not what this BPF assignment computes. For a robust failure verdict, pair the event with the launcher's complete wait status or other independent execution evidence.
Nine observations and the inference each permits
| Trace observation | What is supported | What is not supported |
|---|---|---|
| EXEC for PID 421; later EXIT has exit_code=0 | A scoped, emitted process EXIT row has a high status byte of zero. | That the process exited normally, the command completed its task, or the agent succeeded. |
| Normal exit(7), encoded wait-style status 0x0700 | The BPF expression (status >> 8) & 0xff yields 7. | A cause for the nonzero application exit or which agent decision triggered it. |
| Signal termination with low-byte status 0x000f | The same high-byte extraction yields zero, despite the signal-oriented status. | A normal exit(0). Signal identity is not preserved by this JSON exit_code field. |
| EXIT has no duration_ms | The emission path did not provide a nonzero duration_ns value. | A zero-duration process. A missing exec-start entry can prevent computing the lifetime. |
| Only EXIT appears for a PID | The observed event belongs to that PID and passes the active scope/filter rules. | That the tracer captured the original EXEC. With minimum duration, EXEC output is suppressed. |
| No EXIT for a process | No matching EXIT row was retained in this source/output. | That the process never exited: scope, short-lifetime filtering and ring-buffer reservation matter. |
| One multithreaded program exits | The process-exit probe ignores non-leader thread exit notifications. | A complete thread-by-thread lifecycle or individual worker outcomes. |
| An EXIT row includes vm_hwm_kb | The userspace tracer found a recorded peak-RSS value to attach. | A comparable memory figure on all kernels, machines and processes; the field is conditional. |
| An EXIT row includes ppid and comm | Process and parent identifiers plus a bounded task command name at the probe observation. | A uniquely stable agent task identity, full command history, or causal attribution. |
EXEC and EXIT are deliberately not symmetric
The EXEC hook first stores a monotonic start timestamp keyed by PID. If a minimum lifetime is configured, it may then suppress the EXEC row entirely while retaining that timestamp for the later duration calculation. On exit, the hook ignores non-leader thread exits, looks up the start timestamp, applies its lifetime filter, and attempts to reserve a ring-buffer event. A failed reservation means there may be no emitted EXIT row at all.
If no start timestamp exists, the optional duration can be absent; the userspace serializer prints duration_ms only when the source duration is nonzero. The handler also applies process-tracker scope at readout and can exclude an otherwise observed exit in filtered mode. Therefore an apparently unmatched EXEC or EXIT must be investigated against filtering and capture coverage before diagnosing a process-tree or agent failure.
The emitted comm is a bounded task name, not a reliable substitute for the full command line. The EXEC row has a separate full_commandfield; its underlying collection is itself length-bounded. PID and PPID are useful local process evidence but must be interpreted with lifetime and process-tree context instead of being promoted to a global agent-run identifier.
A reproducible source-only status check
A safe way to verify the exact distinction without capturing prompts or commands is to inspect the pinned hook and serializer, then evaluate the extraction on synthetic integer examples:
git checkout v1.0.35
grep -n "exit_code" bpf/process.bpf.c bpf/process.c
python3 -c "for raw in (0x0000, 0x0700, 0x000f): print(hex(raw), (raw >> 8) & 0xff)"The arithmetic yields 0, 7 and 0 for those respective inputs. The examples use illustrative wait-style words; they are not a live traced process, kernel conformance test, or demonstration that a particular signal is always represented identically on every architecture. For real evidence, inspect a complete launcher wait result alongside scoped AgentSight process events and the command's own pass/fail criteria. Do not put captured arguments, file paths or environment details from a private agent run into public reports.
Process status, tool outcome and task outcome are three levels
A process status tells you how one observed process ended. A tool outcome also needs the relevant stdout/stderr, protocol result or test artifact. An agent-task outcome further needs task acceptance: for example a patch can be generated by a zero-exit process yet fail compilation or review. Conversely, a transient failed process may be retried and recovered by the enclosing agent. Neither case can be decided from one EXIT event.
AgentSight's record command separately waits for the launched child and reports that launcher status; that path should not be confused with the BPF EXIT JSON field for every process in a traced family. For file access rather than process lifetime, use the FILE_OPEN evidence reference. For missing process or TLS observations, retain the capture-loss qualifications. For combining evidence sources, read the audit provenance guide.
Primary sources and interpretation limits
- v1.0.35 process exec/exit BPF hooks, filtering, duration and status extraction
- v1.0.35 userspace JSON serialization and tracked-process filtering
- v1.0.35 process event schema and EXEC/EXIT examples
- v1.0.35 record child-wait/stream shutdown behavior
- Linux wait(2) normal-exit and signal-exit status distinctions
- AgentSight v1.0.35 release
This page documents the current v1.0.35 source contract as reviewed on 31 October 2026. It does not assert complete event delivery, signal-aware exit classification, unbounded command capture, or proof that an entire AI task was correct. A future tracer revision may expose richer raw wait-status evidence; interpret historical rows according to the version that produced them.