1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
[]
= "processkit"
= "0.8.2"
= "2024"
= "Child-process management: kill-on-drop process trees and async run-and-capture"
= "MIT"
= "https://github.com/ZelAnton/ProcessKit-rs"
= "README.md"
= ["process", "subprocess", "job-object", "cgroup", "tokio"]
= ["os", "asynchronous", "command-line-utilities"]
# Keep the published crate lean: ship code, docs, and license — not the
# dev/CI/agent tooling that only matters in the repo.
= [
".claude/",
".github/",
"ideas/",
".editorconfig",
".gitattributes",
"AGENTS.md",
"CLAUDE.md",
"cliff.toml",
"deny.toml",
"rust-toolchain.toml",
]
# Minimum Supported Rust Version. 1.88 is required for `let`-chains
# (`if let … && let …`), used in the unix process-group/cgroup code. The `msrv`
# CI job verifies it — keep the two in sync; raise both when adopting a newer feature.
= "1.88"
# Build docs.rs with every feature so the optional surfaces — `limits`
# (ResourceLimits), the `mock` MockRunner, the `tracing` integration — are
# documented (default features alone hide them).
[]
= true
# Every public type must be `Debug` (consumers derive Debug on structs holding
# ours); warn-level here becomes a hard error under CI's `-D warnings`.
[]
= "warn"
# ---------------------------------------------------------------------------
# Dependencies — every entry carries a "why" (see AGENTS.md "Dependency
# management"). Pin major versions, enable only the features used, keep
# Cargo.lock committed.
# ---------------------------------------------------------------------------
[]
# Async runtime + child-process management: `process` spawns children,
# `time` enforces timeouts, `io-util` streams stdin/stdout line-by-line,
# `rt` powers the background stderr-drain task, `macros` for `#[tokio::main]`
# in examples, `sync` for the oneshot/Notify used in shutdown coordination,
# `fs` for the async file handle behind `Stdin::from_file`,
# `net` for the TcpStream the `wait_for_port` readiness probe connects with.
= { = "1", = ["process", "time", "io-util", "rt", "macros", "sync", "fs", "net"] }
# `async fn` in the object-safe `ProcessRunner` trait so it stays `dyn`-able
# and mockable (native async-in-trait isn't object-safe on our MSRV).
= "0.1"
# Structured `Error` carrying program / exit code / stderr / timeout instead
# of stringly-typed failures; `#[non_exhaustive]` so variants can grow.
= "2"
# Decode child stdout/stderr in non-UTF-8 legacy encodings (Shift-JIS,
# Windows-1252, GBK, …) for the per-stream encoding-override option; default
# stays UTF-8.
= "0.8"
# Adapt tokio's `Lines` reader into an `impl Stream` for the streaming-output
# helper, so callers consume stdout with the standard `Stream` combinators.
# `io-util` gates the `wrappers::LinesStream` adapter we use.
= { = "0.1", = ["io-util"] }
# Optional per-run observability (program/exit code/duration — deliberately
# NOT argv or env, which routinely carry secrets). Off by default; zero cost
# unless the `tracing` feature is enabled.
= { = "0.1", = true }
# Optional auto-generated `MockRunner` for downstream tests. Test-only; pulled
# in solely behind the `mock` feature, never in production builds.
= { = "0.13", = true }
# Optional `CancellationToken` for the `cancellation` feature: structured-
# concurrency cancellation that tears a run's tree down. The `sync` module
# (where CancellationToken lives) is always compiled in tokio-util — no inner
# feature needed; default-features = false pulls in nothing else.
= { = "0.7", = false, = true }
# Optional serialization for the `record` feature's JSON cassettes
# (RecordReplayRunner): `derive` for the cassette structs, serde_json for the
# human-diffable on-disk format. Never in production builds without `record`.
= { = "1", = ["derive"], = true }
= { = "1", = true }
[]
# Both default features are *visibility* gates: additive, hiding API without
# changing the core's semantics — the kill-on-drop tree guarantee is
# unconditional in every configuration. (A broader visibility split was tried
# and deliberately rolled back — decision recorded in
# ideas/architecture-audit-2026-06.md; full analysis in git history,
# ideas/three-layer-resource-split.md.)
= ["stats", "process-control"]
# Resource measurement: `ProcessGroupStats`, `ProcessGroup::stats`, and the
# per-process `RunningProcess::cpu_time`/`peak_memory_bytes` diagnostics.
# Compile it out (`--no-default-features`) to drop the accounting code and, on
# Windows, the ProcessStatus FFI used only for the peak-memory readout.
= ["windows-sys/Win32_System_ProcessStatus"]
# Whole-tree resource caps: `ResourceLimits`, the `memory_max`/`max_processes`/
# `cpu_quota` builders on `ProcessGroupOptions`, and `Error::ResourceLimit`.
# Implies `stats` as policy — resource read and write travel together toward the
# possible `processkit-resource` split (see ideas/) — not as a code dependency.
= ["stats"]
# Tree control beyond contain+kill: `Signal` and
# `ProcessGroup::{signal, suspend, resume, members, adopt}`. Gated because it
# matches the layer a future `processkit-sys` split would carve out.
= []
# Expose the `mockall`-generated `MockRunner` for consumers' tests.
= ["dep:mockall"]
# Emit a `tracing` event per command run (program, exit code, duration —
# argv/env are deliberately not logged: they routinely carry secrets).
= ["dep:tracing"]
# First-class run cancellation: `Command::cancel_on(token)`, `Error::Cancelled`,
# and the `CancellationToken` re-export. Off by default; pulls in `tokio-util`.
= ["dep:tokio-util"]
# Record/replay cassettes over the `ProcessRunner` seam: `RecordReplayRunner`
# records real `Invocation → ProcessResult` pairs to a JSON fixture and replays
# them hermetically. Off by default; pulls in serde + serde_json.
= ["dep:serde", "dep:serde_json"]
[]
# Win32 Job Object FFI for kill-on-close process trees: CreateJobObjectW /
# SetInformationJobObject / AssignProcessToJobObject / TerminateJobObject plus
# the HANDLE type & CloseHandle. Kept on the same major as tokio/mio's copy so
# the lockfile carries a single windows-sys.
= { = "0.61", = [
"Win32_Foundation",
"Win32_System_JobObjects",
# JOBOBJECT_EXTENDED_LIMIT_INFORMATION + GetProcessTimes (per-process CPU);
# also CREATE_SUSPENDED / OpenThread / ResumeThread for race-free containment
# (spawn suspended → assign to job → resume the primary thread).
"Win32_System_Threading",
# CreateJobObjectW's signature references SECURITY_ATTRIBUTES.
"Win32_Security",
# Win32_System_ProcessStatus (K32GetProcessMemoryInfo, peak working set) is
# enabled by the `stats` feature above — it serves only the metrics readout.
# Thread snapshot (CreateToolhelp32Snapshot / Thread32First/Next) to find a
# suspended child's primary thread for ResumeThread — std/tokio expose only
# the process handle, not the PROCESS_INFORMATION thread handle.
"Win32_System_Diagnostics_ToolHelp",
] }
[]
# Raw syscalls std can't express: the POSIX process-group backend (setpgid /
# killpg / kill for teardown and liveness probes) on every unix, plus — on Linux
# — joining cgroup.procs in pre_exec (async-signal-safe getpid/open/write/close).
= "0.2"
[]
# `#[tokio::test]` plus a multi-threaded runtime to drive the async tests;
# `test-util` provides the paused clock for the supervisor's backoff-timing
# tests (virtual time instead of real sleeps); `net` binds the ephemeral
# TcpListener the `wait_for_port` integration test probes against.
= { = "1", = ["macros", "rt-multi-thread", "time", "io-util", "test-util", "net"] }
# Auto-cleaned, parallel-safe temp files for the `record` cassette round-trip
# tests (hand-rolled temp_dir paths leak and race under parallel test runs).
= "3"
[]
# Liveness probe (OpenProcess) in the integration test that proves a grandchild
# is reaped with its job — integration tests can't see the non-dev windows-sys.
= { = "0.61", = ["Win32_Foundation", "Win32_System_Threading"] }
[]
# Liveness probe (kill(pid, 0)) and the root gate (geteuid) in the setsid /
# privilege-drop integration tests — the test crate can't see the non-dev libc.
= "0.2"