Roadmap
Delivered in v0.2.0
- Runnable containment shell.
processkit-cli runexecutes one shell-free command through the publicprocesskitAPI, echoes child stdout/stderr, and preserves the child exit code. Timeouts and cancellation use a distinct, documented runner-owned exit-code band. - JSONL schema v1. The normative event schema and golden fixtures cover
lifecycle events, cleanup, runner failures, and terminal exit. Events are
written to
--jsonl, never stdout, and argv is redacted by default. - Bounded diagnostic capture.
--capture-dirwrites separate bounded stdout/stderr transcripts with full byte counts, hashes, and truncation metadata while preserving live echoed output. - Live-run control plane. The per-user registry and local IPC back
inspect,cancel,kill,list, andprune; stale entries are visible and safely reaped without addressing a process by PID. - End-to-end proof. Through-the-binary tests cover leaked descendants,
nonzero roots, inherited pipe handles, concurrent runs, control-plane
cancellation, and platform-specific teardown behavior. The heavier
e2etier additionally covers abrupt runner death, nested Windows Jobs, PID reuse, and Ctrl-C. - Distribution. Releases publish six prebuilt archives: Windows x86_64 and
aarch64, Linux x86_64 glibc and musl plus aarch64 glibc, and Apple Silicon
macOS. Source installation remains available through
cargo install.
Delivered in v0.2.1
- Explicit stdin sources.
--inherit-stdinshares the runner's input handle with the child, while--stdin-file <file>streams a checked file through ProcessKit and closes stdin at EOF. Closed/null stdin remains the safe default.
Current development
- Interactive inherited stdio.
--inherit-stdiopasses stdin, stdout, and stderr directly to the child, preserving an existing console or terminal while retaining ProcessKit containment, lifecycle JSONL, the control plane, cleanup, and exit-code fidelity. The default remains pipe + echo; transcript capture and no-console mode intentionally conflict with direct inheritance. - Cross-platform terminal proof. Through-the-binary tests cover piped I/O, a real Windows console, and a POSIX pseudo-terminal, including input, terminal detection, JSONL completion, and descendant cleanup.
Remaining ProcessKit-rs dependencies
The processkit 3 graceful-shutdown contract is now fully consumed. Windows console
children can opt into CTRL_BREAK with --windows-graceful-ctrl-break; every
runner-imposed graceful ending probes ProcessGroup::soft_stop_scope() before the
attempt and records the resulting ShutdownReport in
cleanup_finished.shutdown. ProcessGroup::members_info() is likewise consumed
for members_snapshot/inspect enrichment (see
docs/schema.md, "Enriched member fields").
Whole-tree cleanup after an abrupt runner death is also a core dependency on
Unix. The current public primitive kills only the direct child on Linux and is a
no-op on FreeBSD, macOS, and other BSDs; cgroups, process reapers, and process
groups do not disappear with their owner.
Until ProcessKit exposes an additive, identity-safe whole-tree owner-death
primitive, the CLI reports direct_child_only or none in run_started and
does not claim the Windows guarantee on those platforms. Any stronger contract
requires additive, identity-safe ProcessKit-rs support and cross-platform
abrupt-death proof.
Runtime resource-limit attribution is now consumed through processkit 3.2.0.
The existing limit_hit event still covers only the pre-spawn "could not be
applied" branch. Runs with a requested cap additionally emit the additive
limit_evidence event with tripped, not_tripped, or unknown per axis,
read before teardown consumes the group. The evidence is authoritative on
Linux cgroup v2 only: Windows Job Objects, the FreeBSD process reaper, and POSIX
process groups (macOS, other BSDs, and the Linux process-group fallback) do not provide the same successful
Linux path. A successfully created Windows Job Object reports unknown for
capped axes; a POSIX limit request fails before a capped group exists and
therefore emits the existing pre-spawn limit_hit with no limit_evidence.
This closes the runtime-attribution gap on Linux cgroup v2 only; it does not
claim post-run limit attribution on Windows or POSIX fallback runs.
Whole-tree resource measurement — as distinct from that limit attribution — is now
consumed through processkit 3.3.0's ProcessGroup::stats(). Every run that spawned a
child emits the additive resource_summary event exactly once, from the same read point
as limit_evidence and immediately after it, with a slot for peak memory, total CPU, IO
bytes read and written, and peak process count, each independently nullable. This is
deliberately the answer to a different question than limit_hit/limit_evidence — how
much the tree used, not whether a cap engaged — and it is what partially closes the gap
the paragraph above leaves open on Windows: post-run limit attribution there remains
unknown by mechanism, but the measured peak_memory_bytes from the Job Object's own
accounting block is now available as an honest substitute, which is the reason this was
worth taking. The closure is specific to that mechanism and no wider. Axes stay
unavailable by mechanism rather than by omission — peak_process_count on Windows (a Job
Object keeps no peak-concurrency counter), both IO counters on FreeBSD, macOS, other BSDs,
and the Linux
process-group fallback, and on Linux the IO counters additionally need the cgroup v2 io
controller, which this CLI does not enable. On Linux cgroup v2 memory and CPU are also
bounded by the read point rather than by the controller set: stats() sums them from
/proc over the members live when it runs, so a run that ended by its child exiting
reports both as null, and only a runner-imposed ending (read while the tree still runs)
populates them. The normative matrix, including that consequence and why the two
platforms' IO counters are not comparable with each other, is in
docs/resource-limits.md, "What the tree consumed".
That last point is an upstream limitation, not a CLI one, and is recorded here so a
revisit starts from the right place: cgroup v2 does keep whole-tree accumulators that
outlive their members (memory.peak, cpu.stat), but processkit's Linux backend
deliberately folds per-member /proc counters instead, because the cgroup it creates
enables no controller unless a cap asks for one. Closing the gap means asking processkit
for a memory/CPU reading sourced from those controller files (and enabling the controllers
to make them exist), not adding sampling here — a runner-side maximum over its own reads
would report when this CLI looked rather than what the tree did, which the event's contract
rules out.