cargo-hauler 0.4.2 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +110 -36
  3. package/artifact/agent-bundle.manifest.json +1 -1
  4. package/artifact/claude/.claude-plugin/marketplace.json +1 -1
  5. package/artifact/claude/.claude-plugin/plugin.json +1 -1
  6. package/artifact/claude/INSTALL.md +1 -1
  7. package/artifact/claude/bin/cargo-hauler-flight.mjs +645 -214
  8. package/artifact/claude/bin/cargo-hauler.mjs +1495 -675
  9. package/artifact/claude/hooks/event-route-session-start.mjs +21 -11
  10. package/artifact/claude/hooks/event-route-stop.mjs +21 -11
  11. package/artifact/claude/hooks/event-route-tool-after.mjs +21 -11
  12. package/artifact/claude/hooks/event-route-tool-before.mjs +21 -11
  13. package/artifact/claude/hooks/hooks-flight.mjs +674 -200
  14. package/artifact/claude/mcp/mcp-hauler-2b8242f9-flight.mjs +987 -357
  15. package/artifact/claude/mcp/mcp-hauler-2b8242f9.mjs +1398 -295
  16. package/artifact/claude/mcp-apps/dashboard.html +1 -1
  17. package/artifact/claude/scripts/hauler.mjs +1676 -693
  18. package/artifact/claude/skills/cargo-hauler/SKILL.md +11 -6
  19. package/artifact/codex/.codex-plugin/plugin.json +1 -1
  20. package/artifact/codex/INSTALL.md +1 -1
  21. package/artifact/codex/bin/cargo-hauler-flight.mjs +645 -214
  22. package/artifact/codex/bin/cargo-hauler.mjs +1495 -675
  23. package/artifact/codex/hooks/event-route-session-start.mjs +21 -11
  24. package/artifact/codex/hooks/event-route-stop.mjs +21 -11
  25. package/artifact/codex/hooks/event-route-tool-after.mjs +21 -11
  26. package/artifact/codex/hooks/event-route-tool-before.mjs +21 -11
  27. package/artifact/codex/hooks/hooks-flight.mjs +674 -200
  28. package/artifact/codex/mcp/mcp-hauler-2b8242f9-flight.mjs +987 -357
  29. package/artifact/codex/mcp/mcp-hauler-2b8242f9.mjs +1398 -295
  30. package/artifact/codex/mcp-apps/dashboard.html +1 -1
  31. package/artifact/codex/scripts/hauler.mjs +1676 -693
  32. package/artifact/codex/skills/cargo-hauler/SKILL.md +11 -6
  33. package/artifact/cursor/.cursor-plugin/plugin.json +1 -1
  34. package/artifact/cursor/INSTALL.md +1 -1
  35. package/artifact/cursor/bin/cargo-hauler-flight.mjs +645 -214
  36. package/artifact/cursor/bin/cargo-hauler.mjs +1495 -675
  37. package/artifact/cursor/hooks/event-route-session-start.mjs +21 -11
  38. package/artifact/cursor/hooks/event-route-stop.mjs +21 -11
  39. package/artifact/cursor/hooks/event-route-tool-after.mjs +21 -11
  40. package/artifact/cursor/hooks/event-route-tool-before.mjs +21 -11
  41. package/artifact/cursor/hooks/hooks-flight.mjs +674 -200
  42. package/artifact/cursor/install.mjs +1 -1
  43. package/artifact/cursor/mcp/mcp-hauler-2b8242f9-flight.mjs +987 -357
  44. package/artifact/cursor/mcp/mcp-hauler-2b8242f9.mjs +1398 -295
  45. package/artifact/cursor/mcp-apps/dashboard.html +1 -1
  46. package/artifact/cursor/scripts/hauler.mjs +1676 -693
  47. package/artifact/cursor/skills/cargo-hauler/SKILL.md +11 -6
  48. package/artifact/portable/INSTALL.md +1 -1
  49. package/artifact/portable/bin/cargo-hauler-flight.mjs +645 -214
  50. package/artifact/portable/bin/cargo-hauler.mjs +1495 -675
  51. package/artifact/portable/install.mjs +1 -1
  52. package/artifact/portable/mcp/mcp-hauler-2b8242f9-flight.mjs +646 -215
  53. package/artifact/portable/mcp/mcp-hauler-2b8242f9.mjs +1375 -282
  54. package/artifact/portable/mcp-apps/dashboard.html +1 -1
  55. package/artifact/portable/plugin.json +1 -1
  56. package/artifact/portable/scripts/hauler.mjs +1676 -693
  57. package/artifact/portable/skills/cargo-hauler/SKILL.md +11 -6
  58. package/dist/bin/cargo-hauler-flight.mjs +645 -214
  59. package/dist/bin/cargo-hauler-install.js +27 -6
  60. package/dist/bin/cargo-hauler.js +1495 -675
  61. package/dist/bin/hauler.js +1676 -693
  62. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 92e168c: Bump `agent-bundle` and `@agent-bundle/runtime` to the pkg.pr.new preview at `42539ff5f`. With agent-bundle#461 a pass-through `tool/before` result no longer auto-approves, so the hook now answers `allow` only when every command in the input was rewritten onto (or already runs through) the hauler exec path, `continue` + `updatedInput` when a rewritten cargo command shares the input with something the daemon does not govern (`cd … && cargo build`, `cargo test | tail`), and plain `continue` (no decision) for every other tool call; it never returns `ask`. Accept the `confirmed` lineage resolution (agent-bundle#486) in ticket attribution.
8
+
9
+ ## 0.4.3
10
+
11
+ ### Patch Changes
12
+
13
+ - a2c39f3: Bump `agent-bundle` and `@agent-bundle/runtime` to the pkg.pr.new preview at `5775351fb`, which drops the Claude `plugin.json` `hooks` pointer so Claude Code no longer rejects the plugin with "Duplicate hooks file"
14
+ - ccd6a50: Start the auto-spawned daemon with a curated environment (`PATH`, `HOME`, `USER`, `LOGNAME`, `SHELL`, `TMPDIR`, `LANG`/`LC_*`, `XDG_*`, `CARGO_HOME`, `RUSTUP_HOME`, `SSL_CERT_*`, `*_proxy`, `CARGO_HAULER_*`) and the state directory as its cwd, so the first client's `RUSTFLAGS`, `CARGO_TARGET_DIR`, `RUSTC_WRAPPER`, or fd-based `MAKEFLAGS` no longer become the base of every other session's builds. `hauler exec` now relays SIGINT/SIGTERM: a brokered ticket is killed before the client exits `130`/`143`, a direct run's cargo process group is terminated instead of orphaned. A ticket that ends other than `done` prints `ticket cc-N <status>[: reason]` with `128 + signal` for signaled runs; a connection lost after the ack names the ticket (`hauler result cc-N`); a direct run prints its spawn error. The ack carries `waitEtaMs` and auto-background decides on queue wait plus runtime, shown as `wait ~Ns, run ~Ns`. A daemon that never accepted within 60 s goes straight to a direct run instead of a start attempt and a second cycle. `--cwd` is resolved against the caller. `CARGO_HAULER_CARGO_BIN` is read from the daemon's own environment. The PATH shim falls back to the real cargo when its hauler entry is gone and `install-shim` says to re-run `--force` after upgrades. Descriptor-based `MAKEFLAGS`/`MFLAGS`/`CARGO_MAKEFLAGS` jobservers are not transported. Heartbeats count from `started`; a failed or unconfirmed detach is reported; `hauler request --session` no longer holds the stop hook (matching `exec --bg`); `await`/`result`/`kill` fail fast on a daemon `error` reply (#55)
15
+ - ab91d88: Harden the daemon's socket and singleton-lock lifecycle. The daemon now listens on `daemon.sock.<pid>` and atomically renames it over `daemon.sock`, so a daemon that lost socket ownership no longer deletes its replacement's socket on the way out (previously both daemons died and their in-flight tickets were reaped as orphaned). `hauler daemon run` acquires the singleton lock before opening the ledger, running migrations, or draining the passthrough spool, so a losing instance touches neither; it re-checks a held lock about once a second for up to ~20 s instead of giving up on the first look, treats a lock written before the current boot as stale whatever its recorded pid, treats a pid it cannot signal (`EPERM`) as unknown rather than alive, and reclaims a stale lock atomically so two cold starters can no longer both start. A second Ctrl-C during shutdown is now swallowed instead of skipping finalizers and leaving the lock and socket behind (#50)
16
+ - d09b2fb: Harden the `tool/before` shell rewrite and its companions. The rewrite now leaves a command untouched when the pinned `bashjsast` parser cannot round-trip it — background `&`, the `time` keyword, `|&`, `coproc`, and a heredoc feeding a pipeline previously came back as a blocking, un-timed, or syntactically broken command. It no longer passes `--cwd`, so `cd crates/foo && cargo build` builds in `crates/foo`; it skips `command -v/-V cargo`, `type cargo`, and `which cargo`; wraps the unbrokered half of `hauler exec -- cargo build && cargo test`, `while ! cargo build`, and `rustup run <toolchain> -- cargo`. The `cargo clean` guard distinguishes a busy daemon (probe timeout → brokered so the lane serializes the clean) from an absent one (raw run). `tool/after` announces only background or detached tickets, never a foreground run the agent just watched. The stop route clamps `CARGO_HAULER_STOP_WAIT_MS` to the 2 h await ceiling, and `hook-state.json` is written atomically with per-session deny-counter pruning. (#56)
17
+ - df912ae: Honour `hauler kill` for a job parked at the admission gate or on the permit semaphore (it settles `killed` at once instead of blocking its lane until a permit frees), never fold a kill-requested queued job into a batch, clamp the queue ETA so an overrunning lane head no longer cancels queued work and count a head parked at the gate, and finish every settlement step (waiters, lane release, follower exits) even when a ledger write fails. Late attachers receive each replayed chunk exactly once, a follower whose leader exits during registration stays settled (attach/running ledger writes never reopen a terminal row), an early follower release can no longer surface as a `pump failed` cargo termination, and identity attach requires callers to agree on `mergeStderr`. When the shared jobserver FIFO is armed the daemon no longer sets `CARGO_BUILD_JOBS`; `CARGO_HAULER_JOBS_GRANT` applies only while the FIFO is unavailable. (#51, #52, #54)
18
+ - 3f70b60: Keep the daemon responsive and its storage bounded. Kache priors are refreshed without blocking the event loop: the `events.jsonl` tail is read asynchronously from a persisted byte offset and parsed in yielding slices, and the `index.db` aggregate is recomputed only when the file changes on disk. The ledger gains a startup retention pass — finished requests older than `CARGO_HAULER_LEDGER_RETENTION_DAYS` (default `30`) or beyond `CARGO_HAULER_LEDGER_MAX_ROWS` (default `50000`) are deleted with their transitions; `0` disables either limit — the attachment-savings backfill runs once per database (`PRAGMA user_version`) through the rowid index instead of on every open, and request/transition writes commit atomically. NDJSON client lines are capped at 16 MiB: an oversize line gets a `bad-message` error and the connection is closed. Numeric `CARGO_HAULER_*` overrides that do not parse or fall outside their range now log a warning and keep the default instead of silently disabling the arm; `0` or `off` still disables where documented, `CARGO_HAULER_MEM_PRESSURE_SOFT` must stay below `CARGO_HAULER_MEM_PRESSURE_HARD`, and `CARGO_HAULER_BATCH=false|off|no` disables batching like `0` (#57).
19
+ - dd778ab: Fold `cargo test` / `cargo nextest run` requests only when their test selection is identical — same `--test` targets, name filters, arguments after `--`, and nextest filterset — so a composite runs exactly what each participant asked for over the union of their packages (`cargo test -p a` + `cargo test -p b` → `cargo test -p a -p b --no-fail-fast`), never a foreign target or filter. When a composite fails, a folded participant inherits the failure only if it named every package the composite ran; otherwise it is requeued and runs alone instead of reporting another package's failure as its own. Unmodeled post-subcommand options that take a value (`-j`/`--jobs`, `--color`, `--message-format`, `-Z`, `--config`, and nextest's `--retries`, `--test-threads`, `-P`, …) now consume that value instead of recording it as a test-name filter (#53).
20
+
3
21
  ## 0.4.2
4
22
 
5
23
  ### Patch Changes
package/README.md CHANGED
@@ -140,8 +140,8 @@ the same filter as its `session` field). Results carry
140
140
  | `tool:hauler/hauler_request` · `cli:request` | submit a background request | `RequestDocument` |
141
141
  | `cli:daemon` | `run` / `start` / `stop` / `status` | plain JSON, exit code from the result |
142
142
  | `event:session/start` | new session | daemon state and the no-kill rule as context |
143
- | `event:tool/before` | shell tool about to run | rewrites `cargo …` to `hauler exec --session … --host … -- cargo …`; denies `cargo clean` during in-flight builds |
144
- | `event:tool/after` | shell tool finished | injects finished-ticket results once per session |
143
+ | `event:tool/before` | shell tool about to run | rewrites `cargo …` to `hauler exec --session … --host … -- cargo …`; denies `cargo clean` during in-flight builds, brokers it while the daemon is too busy to answer |
144
+ | `event:tool/after` | shell tool finished | injects finished background-ticket results once per session |
145
145
  | `event:stop` | agent stopping | holds the stop while a foreground ticket is pending (bounded, re-deniable) |
146
146
 
147
147
  ### Skills
@@ -216,7 +216,7 @@ executable beside it (`dist/bin/cargo-hauler.js` in the package,
216
216
 
217
217
  | Command | Behavior |
218
218
  | --- | --- |
219
- | `hauler exec [--session ID] [--host HOST] [--cwd DIR] [--bg] -- <cargo …>` | Submit Cargo through the daemon and stream output; hooks rewrite commands to this form. |
219
+ | `hauler exec [--session ID] [--host HOST] [--cwd DIR] [--bg] -- <cargo …>` | Submit Cargo through the daemon and stream output; hooks rewrite commands to this form. A relative `--cwd` is resolved against the caller's directory. Exits with cargo's code; `130`/`143` after a SIGINT/SIGTERM (the ticket is killed first); `75` when auto-backgrounded. |
220
220
  | `hauler status [--limit N] [--cwd DIR] [--session ID] [--lane KEY] [--ticket ID …] [--status S …] [--command-contains TEXT]` | Queue, active runs, lanes, admission, kache, optionally filtered. |
221
221
  | `hauler log [--limit N]` | Recent requests from the ledger. |
222
222
  | `hauler last` | The most recent request. |
@@ -264,9 +264,23 @@ The hook parses the shell command and rewrites each Cargo invocation to
264
264
  `hauler exec --session … --host … -- cargo …`. It recognizes `cargo` behind an
265
265
  absolute path (`~/.cargo/bin/cargo`), and behind the wrappers agents actually
266
266
  use: `env -u VAR X=y cargo …`, `timeout 600 cargo …`,
267
- `rustup run <toolchain> cargo …`, `stdbuf`, `nice`, `ionice`, `nohup`,
268
- `time`, `strace`, `sudo`, `xargs`, `command`, `exec`, and `builtin`. Other
269
- `rustup` subcommands and already-wrapped commands are left alone.
267
+ `rustup run <toolchain> [--] cargo …`, `stdbuf`, `nice`, `ionice`, `nohup`,
268
+ `/usr/bin/time`, `strace`, `sudo`, `xargs`, `command`, `exec`, `builtin`,
269
+ and a negated test (`while ! cargo build; do …`). Other `rustup`
270
+ subcommands, lookups (`command -v cargo`, `type cargo`, `which cargo`), and
271
+ already-wrapped invocations are left alone; in a partially wrapped list
272
+ (`hauler exec -- cargo build && cargo test`) only the unwrapped half is
273
+ rewritten. The rewrite never passes `--cwd`: the command runs in the same
274
+ shell, so `hauler exec` inherits the working directory and
275
+ `cd crates/foo && cargo build` builds in `crates/foo`.
276
+
277
+ Before rewriting, the hook checks that the parser can reproduce the original
278
+ command token for token. Constructs the pinned parser cannot round-trip —
279
+ a background `&` (`nohup cargo build … &`, `cargo build & pid=$!`), the
280
+ `time` keyword, `|&`, `coproc`, a heredoc that feeds a pipeline or is
281
+ followed by another statement, `elif`, and `function name { … }` — are left
282
+ untouched and run as plain Cargo rather than risk emitting a changed
283
+ command.
270
284
 
271
285
  A lane is keyed by workspace root and resolved target directory. It runs one
272
286
  job at a time. Different lanes may run concurrently after acquiring one of the
@@ -288,8 +302,17 @@ Each admitted leader starts one Cargo process. Identity, coverage, and folded
288
302
  batch requests share that process and receive its streamed output. A failed
289
303
  stronger compile does not satisfy a coverage or compile-batch attachment; the
290
304
  attached request returns to its lane unless its required compilation units were
291
- already observed as successful. Folded tests share the composite process,
292
- output, and exit code.
305
+ already observed as successful. Folded tests share the composite process and
306
+ output. `cargo test` / `cargo nextest run` requests fold only when their test
307
+ selection is identical — the same `--test` targets, name filters, arguments
308
+ after `--`, and nextest filterset — so only the package set differs:
309
+ `cargo test -p a` and `cargo test -p b` become
310
+ `cargo test -p a -p b --no-fail-fast`. On success every participant shares
311
+ the exit. When the composite fails, a participant inherits that failure only
312
+ if it named every package the composite ran; otherwise the failing tests may
313
+ belong to another participant's package, so it is requeued and runs alone
314
+ (cargo's test output does not attribute failures to packages). The leader
315
+ keeps the composite exit, as compile-batch leaders do.
293
316
 
294
317
  Brokered output keeps cargo's stdout and stderr as separate channels. When the
295
318
  caller's own stdout and stderr are the same open file (`cargo run 2>&1`, a
@@ -324,18 +347,22 @@ heartbeats. Non-compiling cargo subcommands (`fmt`, `update`, `fetch`, `add`,
324
347
  `remove`, `generate-lockfile`, `vendor`, `new`, `init`, `info`, `uninstall`)
325
348
  run locally instead of queueing for a permit.
326
349
 
327
- The per-run `CARGO_BUILD_JOBS` grant defaults to the available cores divided
328
- across the configured permit count, with a floor of four jobs. Separately, the
329
- daemon arms one GNU make jobserver FIFO with `cores - 1` tokens when it
350
+ The daemon arms one GNU make jobserver FIFO with `cores - 1` tokens when it
330
351
  acquires the singleton lock and passes it to every Cargo it spawns through
331
352
  `MAKEFLAGS`, so concurrent lanes share one global rustc parallelism budget.
353
+ While the FIFO is armed no `CARGO_BUILD_JOBS` is injected, because Cargo only
354
+ joins an inherited jobserver when `-j`/`build.jobs` is unset. The per-run
355
+ `CARGO_BUILD_JOBS` grant — the available cores divided across the configured
356
+ permit count, with a floor of four jobs — is the fallback for a daemon that
357
+ could not arm the FIFO (no `mkfifo`, unwritable state directory). A caller's
358
+ own `-j` flag or `CARGO_BUILD_JOBS` always wins over both.
332
359
 
333
360
  | Capability | Behavior |
334
361
  | --- | --- |
335
362
  | Work sharing | Identical requests attach, covered checks attach, and compatible queued compile or test requests fold. |
336
363
  | Lane isolation | A workspace-root and target-directory pair is serialized independently from other lanes. |
337
364
  | Admission | Per-core load, Linux CPU PSI, Linux memory PSI and `MemAvailable`, macOS VM pressure, configured thresholds, and the global permit cap control new starts. |
338
- | Parallelism | A per-run `CARGO_BUILD_JOBS` grant plus one daemon-owned jobserver FIFO shared by every spawned Cargo. |
365
+ | Parallelism | One daemon-owned jobserver FIFO shared by every spawned Cargo; a per-run `CARGO_BUILD_JOBS` grant only when the FIFO could not be armed. |
339
366
  | Scheduling | EWMA estimates, optional kache priors, fan-out, dependency topology, recent edits, and request age determine lane order. |
340
367
  | Persistence | Tickets, output tails, timings, outcomes, and savings are stored in SQLite. |
341
368
  | Caller output and status | Output streams to attached callers; late callers receive buffered replay. After 30 seconds without output, the client emits a progress heartbeat every 15 seconds with lane queue position, the lane-head ticket, and an aggregate wait ETA. |
@@ -351,19 +378,37 @@ immediately. A synchronous request also switches to background mode when a
351
378
  *measured* estimate — EWMA history or kache priors, never the cold-start
352
379
  default — exceeds the host's shell-tool cap (nine minutes for Claude, ten for
353
380
  Codex, fourteen for Cursor; the PATH shim uses `CARGO_HAULER_HOST` when it is
354
- exported, otherwise the Claude cap). That conversion exits `75`
355
- (`EX_TEMPFAIL`) with the ticket on stderr, so `cargo build && …` chains and
356
- scripts cannot mistake "submitted" for "built"; explicit `--bg` keeps exit
381
+ exported, otherwise the Claude cap). The estimate that is compared is the
382
+ whole wait: the work queued ahead in the lane plus the job's own runtime,
383
+ which the queued line reports as `wait ~Ns, run ~Ns`. That conversion exits
384
+ `75` (`EX_TEMPFAIL`) with the ticket on stderr, so `cargo build && …` chains
385
+ and scripts cannot mistake "submitted" for "built"; explicit `--bg` keeps exit
357
386
  `0`. Failed runs feed the estimate history too, so a broken build is not
358
387
  re-estimated cold on every retry.
359
388
 
360
- The `tool/after` route checks session tickets and, on the first tool call
361
- after a ticket finishes, adds its result to the agent context. For foreground
362
- tickets, the `stop` route waits for the lower of the remaining estimate and
363
- `CARGO_HAULER_STOP_WAIT_MS`; if the ticket finishes it denies the stop and
364
- returns the result, otherwise it denies with status and ETA. `stopHookActive`
365
- and an eight-denial cap per ticket prevent a repeated stop loop; background
366
- tickets never hold a stop. Codex 0.147.0 stop-hold behaviour is verified in
389
+ A foreground `hauler exec` that receives SIGINT or SIGTERM (Ctrl-C, or a
390
+ `timeout N …` wrapper) asks the daemon to kill its ticket, waits for the
391
+ answer, and exits `130` or `143`; in a direct run it terminates the cargo
392
+ process group the same way. A ticket that ends other than `done` is reported
393
+ on stderr as `ticket cc-N <status>[ (signal)][: reason]`, and its exit code is
394
+ cargo's, `128 + signal` for a signaled run, or `1` when the daemon could not
395
+ start cargo at all. If the connection drops after the ticket was accepted, the
396
+ client prints `connection to daemon lost; ticket cc-N continues — hauler
397
+ result cc-N` and exits `1`; the daemon finishes the ticket on its own.
398
+
399
+ The `tool/after` route checks the session's background tickets — `--bg`,
400
+ `hauler_request`, and synchronous requests the client converted to a ticket
401
+ — and, on the first tool call after one finishes, adds its result to the
402
+ agent context. A foreground ticket streamed its exit to the shell the agent
403
+ just watched, so it is never re-announced. For foreground tickets, the
404
+ `stop` route waits for the lower of the remaining estimate and
405
+ `CARGO_HAULER_STOP_WAIT_MS` (clamped to the daemon's two-hour await
406
+ ceiling); if the ticket finishes it denies the stop and returns the result,
407
+ otherwise it denies with status and ETA. `stopHookActive` and an
408
+ eight-denial cap per ticket prevent a repeated stop loop; the per-ticket
409
+ counters live in `hook-state.json`, written atomically and pruned once a
410
+ session's tickets are no longer pending. `--bg` tickets never hold a stop.
411
+ Codex 0.147.0 stop-hold behaviour is verified in
367
412
  [docs/codex-hooks.md](docs/codex-hooks.md).
368
413
 
369
414
  ### PATH shim
@@ -375,7 +420,11 @@ When the daemon starts Cargo it sets `CARGO_HAULER_INSIDE=1`, and the shim then
375
420
  invokes the embedded Cargo directly, so the daemon's own Cargo never returns
376
421
  through the broker. The shim is POSIX-only; its directory must appear before
377
422
  rustup's Cargo directory on `PATH`; replacing an existing destination requires
378
- `--force`.
423
+ `--force`. The embedded `hauler` entry lives in a versioned plugin directory:
424
+ when that file no longer exists (an upgrade replaced the directory), the shim
425
+ runs the embedded Cargo directly instead of failing, and `install-shim` says
426
+ so — re-run `hauler install-shim --force` after such an upgrade to route
427
+ scripted Cargo through the broker again.
379
428
 
380
429
  ### Caller environment
381
430
 
@@ -388,10 +437,24 @@ Cargo, so `FOO=bar cargo build` reaches `build.rs`, `env!()`, `cargo run`, and
388
437
  for coalescing is digested from the build-relevant subset only (`CARGO_*`,
389
438
  `RUST*`, `CC`/`CXX`/`AR`/`CFLAGS`/`CXXFLAGS`/`LDFLAGS` with target-suffixed
390
439
  forms, and `PKG_CONFIG_PATH`); pass knobs a `build.rs` reads through
391
- `--config 'env.FOO="bar"'` when they must also split identity.
440
+ `--config 'env.FOO="bar"'` when they must also split identity. One value is
441
+ filtered rather than forwarded: a `MAKEFLAGS`, `MFLAGS`, or `CARGO_MAKEFLAGS`
442
+ carrying a descriptor-based jobserver (`--jobserver-auth=R,W`,
443
+ `--jobserver-fds=R,W`) names file descriptors that exist only in the caller,
444
+ so it is dropped and the daemon's shared FIFO jobserver applies; a
445
+ `fifo:PATH` jobserver travels as-is.
392
446
  `hauler request` and `hauler_request` submit without a caller environment;
393
447
  their Cargo processes run with the daemon's environment.
394
448
 
449
+ The daemon's own environment is deliberately small. When a client starts it,
450
+ the daemon receives only `PATH`, `HOME`, `USER`, `LOGNAME`, `SHELL`, `TMPDIR`,
451
+ `LANG` and `LC_*`, `XDG_*`, `CARGO_HOME`, `RUSTUP_HOME`, `SSL_CERT_*`, the
452
+ `*_proxy` variables, and every `CARGO_HAULER_*` setting, with the state
453
+ directory as its working directory. The starting shell's `RUSTFLAGS`,
454
+ `CARGO_TARGET_DIR`, `RUSTC_WRAPPER`, `CARGO_BUILD_*`, `MAKEFLAGS`, `CC`, and
455
+ similar build knobs are not inherited, so they cannot become the silent base of
456
+ every other session's builds.
457
+
395
458
  ### Kache integration
396
459
 
397
460
  When [kache](https://github.com/ScriptedAlchemy/kache) is available,
@@ -407,27 +470,32 @@ is reported as unavailable and never rejects a request.
407
470
  | Variable | Default | Meaning |
408
471
  | --- | --- | --- |
409
472
  | `CARGO_HAULER_STATE_DIR` | Per-user cache directory | Unix socket or Windows named pipe source, SQLite ledger, daemon log, pid lock, `hook-state.json`, and `hook-events.jsonl`. No legacy alias. |
410
- | `CARGO_HAULER_CARGO_BIN` | `$CARGO_HOME/bin/cargo` | Cargo binary for daemon-started work; bare `cargo` is the last fallback. Never resolved through `PATH`. |
411
- | `CARGO_HAULER_MAX_CONCURRENT` | `5` | Global admission permits for Cargo processes across all lanes. |
412
- | `CARGO_HAULER_JOBS_GRANT` | `max(4, cores / max concurrent)` | `CARGO_BUILD_JOBS` added to each Cargo process; `0` disables injection. |
473
+ | `CARGO_HAULER_CARGO_BIN` | `$CARGO_HOME/bin/cargo` | Cargo binary for daemon-started work; bare `cargo` is the last fallback. Never resolved through `PATH`. Read from the daemon's own environment (export it where the daemon starts, or before `hauler daemon start`); clients do not forward it. |
474
+ | `CARGO_HAULER_MAX_CONCURRENT` | `5` | Global admission permits for Cargo processes across all lanes; an integer >= 1. |
475
+ | `CARGO_HAULER_JOBS_GRANT` | `max(4, cores / max concurrent)` | `CARGO_BUILD_JOBS` added to each Cargo process only while the shared jobserver FIFO is not armed; an armed daemon injects `MAKEFLAGS` instead and leaves `CARGO_BUILD_JOBS` unset. `0` disables injection. |
413
476
  | `CARGO_HAULER_LOAD_THRESHOLD` | Disabled | Per-core one-minute load threshold for deferring new admissions. |
414
477
  | `CARGO_HAULER_LOAD_MIN` | `2` | Active Cargo processes below which load, CPU PSI, and soft memory pressure do not defer admission. |
415
- | `CARGO_HAULER_CPU_PRESSURE_THRESHOLD` | `75` | Linux CPU PSI `some avg10` percentage for deferring new admissions; `0` disables. |
416
- | `CARGO_HAULER_MEM_PRESSURE_SOFT` | `10` (Linux) | Memory PSI `full avg10` percentage for soft deferral; `0` disables. |
417
- | `CARGO_HAULER_MEM_PRESSURE_HARD` | `20` (Linux) | Memory PSI `full avg10` percentage for hard deferral, confirmed by `full avg60` at half the value; `0` disables. |
418
- | `CARGO_HAULER_MEM_AVAILABLE_MIN_GB` | `8` (Linux) | `MemAvailable` floor in GiB for hard deferral; `0` disables. |
419
- | `CARGO_HAULER_MEM_PRESSURE_LEVEL` | `2` (macOS) | Kernel VM pressure level that starts soft deferral (`2` warn, `4` critical). |
478
+ | `CARGO_HAULER_CPU_PRESSURE_THRESHOLD` | `75` | Linux CPU PSI `some avg10` percentage for deferring new admissions; `0` or `off` disables. |
479
+ | `CARGO_HAULER_MEM_PRESSURE_SOFT` | `10` (Linux) | Memory PSI `full avg10` percentage for soft deferral; `0` or `off` disables. Must stay below the hard threshold, otherwise both revert to their defaults. |
480
+ | `CARGO_HAULER_MEM_PRESSURE_HARD` | `20` (Linux) | Memory PSI `full avg10` percentage for hard deferral, confirmed by `full avg60` at half the value; `0` or `off` disables. |
481
+ | `CARGO_HAULER_MEM_AVAILABLE_MIN_GB` | `8` (Linux) | `MemAvailable` floor in GiB for hard deferral; `0` or `off` disables. |
482
+ | `CARGO_HAULER_MEM_PRESSURE_LEVEL` | `2` (macOS) | Kernel VM pressure level that starts soft deferral (`2` warn, `4` critical); `0` or `off` disables. |
420
483
  | `CARGO_HAULER_HEAVY_MEM_AVAILABLE_GB` | `16` (Linux) | `MemAvailable` in GiB below which concurrent heavy leaders (release/perf/bench profiles, workspace-wide runs) are capped; `0` or `off` disables the cap. |
421
484
  | `CARGO_HAULER_HEAVY_MAX_CONCURRENT` | `1` | Heavy leaders admitted at once while the cap is active. |
422
485
  | `CARGO_HAULER_REPLAY_BUFFER_BYTES` | `4194304` | Leader output retained in memory for late-attacher replay. |
423
486
  | `CARGO_HAULER_KACHE_INDEX` | kache's configured store | kache index for per-crate timing priors; an empty string disables it. |
424
- | `CARGO_HAULER_BATCH` | Enabled | `0` disables the batch composer. |
487
+ | `CARGO_HAULER_BATCH` | Enabled | `0`, `false`, `off`, or `no` disables the batch composer. |
425
488
  | `CARGO_HAULER_BATCH_WINDOW_MS` | `150` | Delay applied to a batchable lane head so nearby requests can fold; `0` disables. |
426
489
  | `CARGO_HAULER_KILL_GRACE_MS` | `8000` | Time between SIGTERM and SIGKILL when the daemon stops a Cargo process. |
427
- | `CARGO_HAULER_STOP_WAIT_MS` | `30000` | Maximum wait for one stop-hook invocation. |
490
+ | `CARGO_HAULER_STOP_WAIT_MS` | `30000` | Maximum wait for one stop-hook invocation; values above the 7200000 ms await ceiling are clamped. |
491
+ | `CARGO_HAULER_LEDGER_RETENTION_DAYS` | `30` | Finished ledger rows older than this many days are deleted when the daemon starts; `0` disables the age limit. |
492
+ | `CARGO_HAULER_LEDGER_MAX_ROWS` | `50000` | Total ledger rows beyond which the oldest finished rows are deleted when the daemon starts; `0` disables the row cap. |
428
493
  | `CARGO_HAULER_LOG_LEVEL` | `Info` | Daemon log level. |
429
494
  | `CARGO_HAULER_HOST`, `CARGO_HAULER_SESSION` | Unset | Default `--host` and `--session` attribution for `hauler exec`; the PATH shim also borrows `CARGO_HAULER_HOST`'s shell cap for auto-background. |
430
495
 
496
+ A numeric value that does not parse or falls outside its range is reported
497
+ as a warning (daemon log, or stderr for hand-run commands) and the default
498
+ applies; only `0` or `off` disables an arm that documents that contract.
431
499
  Each `CARGO_HAULER_*` tuning value takes precedence over its retained legacy
432
500
  `CARGO_CONDUCTOR_*` alias; `CARGO_CONDUCTOR_STATE_DIR` is ignored and hand-run
433
501
  commands warn when it is still exported (see
@@ -445,7 +513,8 @@ unset, the daemon reads kache's configured local store from
445
513
  command through, and a client that cannot reach the daemon makes one
446
514
  auto-start attempt and then invokes Cargo directly. A daemon that is alive
447
515
  but too loaded to accept within 2 seconds is not treated as absent: `exec`
448
- retries for up to 60 seconds before falling back to a direct run.
516
+ retries for up to 60 seconds, then runs Cargo directly without a start
517
+ attempt or a second retry cycle.
449
518
  - The plugin's own documents never fail open: `hauler_result` and
450
519
  `hauler_await` fail loudly when the daemon is unreachable instead of
451
520
  reporting a ticket as not found; `hauler_status`, `hauler_log`, and
@@ -454,6 +523,11 @@ unset, the daemon reads kache's configured local store from
454
523
  - Test sharing uses identity attachment or batch folding, never coverage.
455
524
  Folded `test` and `nextest` requests receive the composite output and exit
456
525
  code, so a failure may come from another package in the batch.
526
+ - The `cargo clean` guard probes the daemon for 250 ms. Active work denies
527
+ the clean; an idle daemon brokers it; a daemon that accepts but does not
528
+ answer in time is busy, so the clean is brokered and the lane serializes
529
+ it behind the builds it would otherwise race; only a socket nobody listens
530
+ on (`ECONNREFUSED`, `ENOENT`) lets a raw `cargo clean` run.
457
531
  - Hook rewrites, policy denials such as `cargo clean` during an active build,
458
532
  and malformed requests are recorded (`hook-events.jsonl`; a failed ledger
459
533
  row).
@@ -478,7 +552,7 @@ data is the daemon's own. The repository ships no preview harness of its own.
478
552
 
479
553
  agent-bundle does not yet have an npm release; this repository pins the
480
554
  [pkg.pr.new](https://pkg.pr.new) preview of main commit
481
- [`886b1921f`](https://github.com/ScriptedAlchemy/agent-bundle/commit/886b1921f64f7b857528acda32d94c4d0df9bba7)
555
+ [`42539ff5f`](https://github.com/ScriptedAlchemy/agent-bundle/commit/42539ff5fbafdff0656317b841a50100ac0fc141)
482
556
  for both `agent-bundle` and `@agent-bundle/runtime`. `inspect` reports the
483
557
  `agent` component kind as unavailable on every host (agent-bundle G5
484
558
  deferral); this plugin defines no agents. Two framework limitations observed