cargo-hauler 0.4.4 → 0.4.6
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.
- package/CHANGELOG.md +16 -0
- package/README.md +108 -20
- package/artifact/agent-bundle.manifest.json +1 -1
- package/artifact/claude/.claude-plugin/marketplace.json +1 -1
- package/artifact/claude/.claude-plugin/plugin.json +1 -1
- package/artifact/claude/INSTALL.md +1 -1
- package/artifact/claude/bin/cargo-hauler-flight.mjs +5325 -4253
- package/artifact/claude/bin/cargo-hauler.mjs +5849 -4228
- package/artifact/claude/hooks/event-route-session-start.mjs +2 -2
- package/artifact/claude/hooks/event-route-stop.mjs +2 -2
- package/artifact/claude/hooks/event-route-tool-after.mjs +2 -2
- package/artifact/claude/hooks/event-route-tool-before.mjs +2 -2
- package/artifact/claude/hooks/hooks-flight.mjs +776 -287
- package/artifact/claude/mcp/mcp-hauler-2b8242f9-flight.mjs +7554 -6597
- package/artifact/claude/mcp/mcp-hauler-2b8242f9.mjs +6239 -5297
- package/artifact/claude/mcp-apps/dashboard.html +1 -1
- package/artifact/claude/scripts/hauler.mjs +2170 -572
- package/artifact/claude/skills/cargo-hauler/SKILL.md +32 -0
- package/artifact/codex/.codex-plugin/plugin.json +1 -1
- package/artifact/codex/INSTALL.md +1 -1
- package/artifact/codex/bin/cargo-hauler-flight.mjs +5325 -4253
- package/artifact/codex/bin/cargo-hauler.mjs +5849 -4228
- package/artifact/codex/hooks/event-route-session-start.mjs +2 -2
- package/artifact/codex/hooks/event-route-stop.mjs +2 -2
- package/artifact/codex/hooks/event-route-tool-after.mjs +2 -2
- package/artifact/codex/hooks/event-route-tool-before.mjs +2 -2
- package/artifact/codex/hooks/hooks-flight.mjs +776 -287
- package/artifact/codex/mcp/mcp-hauler-2b8242f9-flight.mjs +7554 -6597
- package/artifact/codex/mcp/mcp-hauler-2b8242f9.mjs +6239 -5297
- package/artifact/codex/mcp-apps/dashboard.html +1 -1
- package/artifact/codex/scripts/hauler.mjs +2170 -572
- package/artifact/codex/skills/cargo-hauler/SKILL.md +32 -0
- package/artifact/cursor/.cursor-plugin/plugin.json +1 -1
- package/artifact/cursor/INSTALL.md +1 -1
- package/artifact/cursor/bin/cargo-hauler-flight.mjs +5325 -4253
- package/artifact/cursor/bin/cargo-hauler.mjs +5849 -4228
- package/artifact/cursor/hooks/event-route-session-start.mjs +2 -2
- package/artifact/cursor/hooks/event-route-stop.mjs +2 -2
- package/artifact/cursor/hooks/event-route-tool-after.mjs +2 -2
- package/artifact/cursor/hooks/event-route-tool-before.mjs +2 -2
- package/artifact/cursor/hooks/hooks-flight.mjs +776 -287
- package/artifact/cursor/install.mjs +1 -1
- package/artifact/cursor/mcp/mcp-hauler-2b8242f9-flight.mjs +7554 -6597
- package/artifact/cursor/mcp/mcp-hauler-2b8242f9.mjs +6239 -5297
- package/artifact/cursor/mcp-apps/dashboard.html +1 -1
- package/artifact/cursor/scripts/hauler.mjs +2170 -572
- package/artifact/cursor/skills/cargo-hauler/SKILL.md +32 -0
- package/artifact/portable/INSTALL.md +1 -1
- package/artifact/portable/bin/cargo-hauler-flight.mjs +5325 -4253
- package/artifact/portable/bin/cargo-hauler.mjs +5849 -4228
- package/artifact/portable/install.mjs +1 -1
- package/artifact/portable/mcp/mcp-hauler-2b8242f9-flight.mjs +7537 -6580
- package/artifact/portable/mcp/mcp-hauler-2b8242f9.mjs +6235 -5293
- package/artifact/portable/mcp-apps/dashboard.html +1 -1
- package/artifact/portable/plugin.json +1 -1
- package/artifact/portable/scripts/hauler.mjs +2170 -572
- package/artifact/portable/skills/cargo-hauler/SKILL.md +32 -0
- package/dist/bin/cargo-hauler-flight.mjs +5325 -4253
- package/dist/bin/cargo-hauler.js +5849 -4228
- package/dist/bin/hauler.js +2170 -572
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 4257982: Survive a CLI or plugin upgrade under a daemon left running from the older install, and add `hauler daemon restart`. The client schemas now default the fields older daemons never send (`outputPath`, `after`), so `hauler status`, `hauler result`, `hauler await`, and the `hauler_*` tools read a 0.4.1 daemon's replies instead of printing a Zod issue array; a reply that still cannot be read fails as `daemon is 0.4.1 (pid N, since 3h ago), this CLI is 0.4.4 — restart it with \`hauler daemon restart\`` with the first mismatch (`active[0].outputPath expected string, received undefined`) on a second line, and `hauler daemon status` says the same. The status report carries the daemon `version` (older daemons are asked with one `ping`), `hauler status --json` exposes it as `daemonVersion`, and every document header — plus the status summary — warns `daemon 0.4.2 ≠ cli 0.4.4 — restart it with \`hauler daemon restart\`` while the versions differ. `hauler daemon restart` sends the graceful stop, waits up to 5 s for the old pid to exit, starts a daemon from this install, and prints both pids and versions (`previousPid` in the JSON result; exit `1` when the old daemon did not exit, in which case nothing is started). Tickets in flight at a restart are not handed over: the new daemon marks them `killed` with `orphaned by daemon restart`, and `hauler result` / `hauler_result` explain that instead of showing a plain kill. The shell header ends with `state dir <path>` so a moved `CARGO_HAULER_STATE_DIR` is visible on every command. (#75)
|
|
8
|
+
|
|
9
|
+
## 0.4.5
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 62d4e16: Add `--after <ticket>[,<ticket>…]` to `hauler exec` and `hauler request` (and `after: string[]` on `hauler_request`) to declare explicit ticket dependencies: the request stays `queued` — skipped by lane admission and batch folding — until every named ticket has settled, fails with `prerequisite cc-N failed`/`killed` (exit code `null`) if one of them does, resolves at once when they already finished, and is rejected as a `bad-intent` error naming an unknown ticket. Prerequisites may live in another lane; the ledger stores them (`after` on every request record) and `hauler status`/`hauler result`/await heartbeats show `waits for cc-N (running 2m/~5m)` while a dependent is held. The `exec --bg` and `request` acknowledgements now name the tickets ahead in the lane (`queued behind cc-3281 (~13m)`, `ahead` on the ack and `queue` on the request result) so a cost-ordered reorder is visible, and `hauler request` reports the daemon's rejection reason instead of a generic submit failure. (#45)
|
|
14
|
+
- 1135585: The daemon rejects a path-shaped program that is not cargo (`bad-intent`) instead of running it and recording the path as the subcommand; the dashboard's by-command table shows legacy rows by basename, and the all-time latency tile reads "latency added by attaching" when riders waited longer than their solo estimate instead of showing a negative "saved" value. Dashboard screenshots refreshed.
|
|
15
|
+
- d86d8b6: The shared fifo jobserver is armed only when the host `make` can speak it (GNU make 4.4+, or no make at all); on older makes the daemon falls back to per-run `CARGO_BUILD_JOBS` grants so `-sys` crates whose build scripts run `make` (jemalloc, openssl, …) build again. `CARGO_HAULER_JOBSERVER=fifo|off|auto` overrides the detection (#76).
|
|
16
|
+
- b13db35: Detect stalled tickets and end orphaned ones. The daemon samples every running ticket's process-tree CPU time (Linux `/proc`, macOS `ps`) and flags a run `stalled` once it is past `CARGO_HAULER_STALL_ESTIMATE_FACTOR` (3) times its estimate with no CPU and no output for `CARGO_HAULER_STALL_IDLE_MS` (10 min). `hauler status` / `hauler_status`, the dashboard, and `hauler await` heartbeats show `stalled` with the idle duration; `hauler result` / `hauler_result` answer `ticket looks stalled (no CPU for Nm) — hauler kill cc-N`. A stalled ticket whose submitting connection has disconnected is killed automatically through the `hauler kill` path with the error `stalled: no CPU for Nm after owner disconnected; killed automatically`; `CARGO_HAULER_STALL_AUTO_KILL=0` only flags. Status records gain optional `stall` and `orphaned` fields. (#46)
|
|
17
|
+
- cfb6967: Keep every ticket's whole output on disk and make it readable after the fact. The daemon writes a leader run's combined stdout+stderr, as emitted (rendered diagnostics for demultiplexed runs, ANSI as captured), to `<state dir>/tickets/<ticket>.log`, bounded by the new `CARGO_HAULER_TICKET_LOG_MAX_BYTES` (default 64 MiB, then one truncation line; `0` disables). The ledger gains `output_path`, exposed as `outputPath` on every request record (attached followers carry their leader's path); the startup retention pass removes the logs of pruned rows and any log without a row. `hauler result <ticket>` now shows `Full output: <path> (size)` and `--json` carries `request.outputPath`; `hauler result <ticket> --full` and `hauler_result { ticket, full: true }` render the whole log as the document body (the last ~768 KiB when it does not fit, with the path for the rest). When a synchronous command is auto-backgrounded and the caller's stdout is not a terminal, the exit-75 notice adds that the redirect receives no output and to read it with `hauler result cc-N --full`. (#68)
|
|
18
|
+
|
|
3
19
|
## 0.4.4
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -41,11 +41,14 @@ src/
|
|
|
41
41
|
Every rendered route — MCP tool, CLI command, rendered script — composes
|
|
42
42
|
through one layout, the way a page framework's `layout.tsx` wraps every page:
|
|
43
43
|
|
|
44
|
-
- **Header:** `<DaemonBadge>` prints what the request-start probe proved
|
|
45
|
-
`cargo-hauler · daemon running (pid 4021) ·
|
|
46
|
-
· 2 lanes busy · up since 3h ago
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
- **Header:** `<DaemonBadge>` prints what the request-start probe proved and
|
|
45
|
+
which state directory it is: `cargo-hauler · daemon running (pid 4021) ·
|
|
46
|
+
2/5 permits +1 riding, 1 queued · 2 lanes busy · up since 3h ago · state dir
|
|
47
|
+
/fast/cache/cargo-hauler`, or `daemon stopped · no socket; it starts on
|
|
48
|
+
demand…`, or `daemon unresponsive · did not accept a connection within
|
|
49
|
+
750ms (machine saturated)…`. When the daemon is another build than the CLI
|
|
50
|
+
or MCP server rendering the document, a second line says so: `cargo-hauler
|
|
51
|
+
· daemon 0.4.2 ≠ cli 0.4.4 — restart it with \`hauler daemon restart\``.
|
|
49
52
|
- **Body:** the route's own document, unchanged. The route keeps its
|
|
50
53
|
`<Agent.Result value>`; the runtime merges it into the shell so
|
|
51
54
|
`structuredContent` and `--json` are exactly what the route declared.
|
|
@@ -66,7 +69,7 @@ and a `health` value from one bounded `status` probe:
|
|
|
66
69
|
|
|
67
70
|
| `health.state` | meaning |
|
|
68
71
|
| --- | --- |
|
|
69
|
-
| `running` | `pid`, `startedAtMs`, `latencyMs`, `running` (permit holders), `riding` (attached), `queued`, `busyLanes`, `maxConcurrent` |
|
|
72
|
+
| `running` | `pid`, `startedAtMs`, `latencyMs`, `running` (permit holders), `riding` (attached), `queued`, `busyLanes`, `maxConcurrent`, and `version` when the daemon states one (on the status report from 0.4.5, by one extra `ping` for older daemons) |
|
|
70
73
|
| `stopped` | `socket-missing` (starts on demand) or `connection-refused` (stale socket) |
|
|
71
74
|
| `unresponsive` | `accept-timeout` (never accepted), `answer-timeout` (accepted, no `status-result`), or `connection-closed` within the probe budget (750 ms for the accept and for the answer); ledger reads still work |
|
|
72
75
|
| `unreachable` | `open-failed` with the errno (`EACCES`, `EMFILE`, …): the socket is present but could not be opened, which is not evidence the daemon is down |
|
|
@@ -90,6 +93,7 @@ assertion share one derivation.
|
|
|
90
93
|
| `<AdmissionState>` | permits in use, load, memory clamp, sharing savings; calls out a paused admission gate |
|
|
91
94
|
| `<KacheStats>` | kache coverage and freshness, slowest crates by profile, or an honest "not detected" |
|
|
92
95
|
| `<LogTail>` | the captured output tail, labelled live while the run is in progress |
|
|
96
|
+
| `<FullOutput>` | where the ticket's whole output log lives and how large it is; under `full`, the log itself in code-block chunks |
|
|
93
97
|
| `<BuildDiagnostics>` | an index of cargo `error[E…]`/`warning:` blocks (level / code / message / location) followed by every captured block verbatim |
|
|
94
98
|
| `<DashboardLink>` | where the MCP App lives and how to open it elsewhere |
|
|
95
99
|
| `<TicketGuidance>` | what to do next, one component per ticket status |
|
|
@@ -135,7 +139,7 @@ the same filter as its `session` field). Results carry
|
|
|
135
139
|
| `tool:hauler/hauler_log` · `cli:log` | recent requests | `LogStream` → `LogDocument` |
|
|
136
140
|
| `tool:hauler/hauler_last` · `cli:last` | most recent request | `LastDocument` |
|
|
137
141
|
| `tool:hauler/hauler_await` · `cli:await` | long-poll a ticket (≤ 2 h) | `AwaitStream` → `AwaitDocument` |
|
|
138
|
-
| `tool:hauler/hauler_result` · `cli:result` | one ticket, live tail while running | `ResultDocument` |
|
|
142
|
+
| `tool:hauler/hauler_result` · `cli:result` | one ticket, live tail while running; `full` renders the whole on-disk output log | `ResultDocument` (`<FullOutput>`) |
|
|
139
143
|
| `tool:hauler/hauler_kill` · `cli:kill` | stop a queued or running ticket | `KillDocument` |
|
|
140
144
|
| `tool:hauler/hauler_request` · `cli:request` | submit a background request | `RequestDocument` |
|
|
141
145
|
| `cli:daemon` | `run` / `start` / `stop` / `status` | plain JSON, exit code from the result |
|
|
@@ -216,15 +220,15 @@ executable beside it (`dist/bin/cargo-hauler.js` in the package,
|
|
|
216
220
|
|
|
217
221
|
| Command | Behavior |
|
|
218
222
|
| --- | --- |
|
|
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. |
|
|
223
|
+
| `hauler exec [--session ID] [--host HOST] [--cwd DIR] [--bg] [--after TICKET[,TICKET…]] -- <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. `--after` (repeatable or comma-separated) keeps the request queued until every named ticket has finished; it fails with `prerequisite cc-N <status>` if one of them fails or is killed, and an unknown ticket is rejected as a bad intent. Exits with cargo's code; `130`/`143` after a SIGINT/SIGTERM (the ticket is killed first); `75` when auto-backgrounded. |
|
|
220
224
|
| `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
225
|
| `hauler log [--limit N]` | Recent requests from the ledger. |
|
|
222
226
|
| `hauler last` | The most recent request. |
|
|
223
227
|
| `hauler await <ticket> [--max-wait-ms N]` | Long-poll until the ticket finishes or the wait expires (default 30 s, ceiling 55 s per call — the rendered-route budget; call again to keep waiting). |
|
|
224
|
-
| `hauler result <ticket
|
|
228
|
+
| `hauler result <ticket> [--full]` | A stored ticket; running tickets include a live output tail. The document names the full on-disk output log (`Full output: <path> (size)`) and `--json` carries it as `request.outputPath`; `--full` prints that whole log instead of the tail (the last ~768 KiB when it does not fit, with the path for the rest). |
|
|
225
229
|
| `hauler kill <ticket>` | Stop a ticket: drop it from the queue or SIGTERM (then SIGKILL) its cargo process group, freeing the lane. Riders return to their lane or fail with it. |
|
|
226
|
-
| `hauler request [--session ID] [--host HOST] [--cwd DIR] -- <cargo …>` | Submit a background request and return its ticket. |
|
|
227
|
-
| `hauler daemon <run\|start\|stop\|status>` | Manage the daemon lifecycle. |
|
|
230
|
+
| `hauler request [--session ID] [--host HOST] [--cwd DIR] [--after TICKET …] -- <cargo …>` | Submit a background request and return its ticket, with where it landed in its lane (`queued behind cc-3281 (~13m)`, `waiting for cc-3281`, or `attached to cc-3281`). `--after` works as for `exec`. |
|
|
231
|
+
| `hauler daemon <run\|start\|stop\|status\|restart>` | Manage the daemon lifecycle. `restart` sends the graceful stop, waits up to 5 s for the old pid to exit, then starts a daemon from this install and prints both (`restarted: pid 741314 (0.4.1) → pid 742001 (0.4.4)`); a daemon that has not exited by then is reported, not killed, and nothing is started (exit `1`). Tickets in flight at the restart are not handed over: the new daemon marks them `killed` with the error `orphaned by daemon restart`. |
|
|
228
232
|
| `hauler install-shim [--dir DIR] [--real-cargo PATH] [--force]` | Install the optional PATH shim. |
|
|
229
233
|
|
|
230
234
|
The `hauler` MCP server projects the same operations as `hauler_status`,
|
|
@@ -330,6 +334,26 @@ callers, dependency-unblocking work, and recently edited packages receive a
|
|
|
330
334
|
lower scheduling score. Waiting time lowers the score further so broad work
|
|
331
335
|
eventually runs.
|
|
332
336
|
|
|
337
|
+
Admission within a lane is therefore cost-ordered, not first-in-first-out: a
|
|
338
|
+
queued `cargo test -p foo` (a cheap estimate) normally starts before a queued
|
|
339
|
+
`cargo build --workspace` submitted a minute earlier, even when the test
|
|
340
|
+
spawns a binary that build produces. The acknowledgement makes the order
|
|
341
|
+
visible — `ticket cc-3289 queued behind cc-3281 (1 ahead, wait ~780s)` on
|
|
342
|
+
`exec`, `cc-3289 submitted, queued behind cc-3281 (~13m)` on `request` — and
|
|
343
|
+
`--after` makes the dependency explicit: `hauler exec --after cc-3281 -- cargo
|
|
344
|
+
test …` (or `hauler request --after`, or `after: ["cc-3281"]` on
|
|
345
|
+
`hauler_request`) stays `queued` and is skipped by admission and batch folding
|
|
346
|
+
until every named ticket has settled. Prerequisites may live in any lane; a
|
|
347
|
+
prerequisite in the same lane also scores higher while a ticket waits on it,
|
|
348
|
+
like a dependency-closure leaf. A prerequisite
|
|
349
|
+
that ends `failed` or `killed` settles the dependent `failed` with
|
|
350
|
+
`prerequisite cc-N failed` and no exit code, without spawning cargo; its riders
|
|
351
|
+
follow the normal attachment rules. A prerequisite that already finished
|
|
352
|
+
resolves immediately; an unknown ticket is rejected as a bad intent. A blocked
|
|
353
|
+
ticket never attaches to a run already in flight (that run started before the
|
|
354
|
+
prerequisite finished), and `hauler status`, `hauler result`, and await
|
|
355
|
+
heartbeats show `waits for cc-N (running 2m/~5m)` while it is held.
|
|
356
|
+
|
|
333
357
|
Admission is separate from lane scheduling. It observes one-minute load per
|
|
334
358
|
core and, on Linux, CPU PSI `some avg10`, then applies the configured thresholds
|
|
335
359
|
and the global permit cap. Load and CPU pressure never defer below
|
|
@@ -363,18 +387,31 @@ own `-j` flag or `CARGO_BUILD_JOBS` always wins over both.
|
|
|
363
387
|
| Lane isolation | A workspace-root and target-directory pair is serialized independently from other lanes. |
|
|
364
388
|
| 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. |
|
|
365
389
|
| 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. |
|
|
366
|
-
| Scheduling | EWMA estimates, optional kache priors, fan-out, dependency topology, recent edits, and request age determine lane order. |
|
|
367
|
-
| Persistence | Tickets, output tails, timings, outcomes, and savings are stored in SQLite
|
|
390
|
+
| Scheduling | EWMA estimates, optional kache priors, fan-out, dependency topology, recent edits, and request age determine lane order; `--after cc-N` holds a request until the named tickets settle. |
|
|
391
|
+
| Persistence | Tickets, output tails, timings, outcomes, and savings are stored in SQLite; every leader run's whole combined output is kept on disk as `<state dir>/tickets/<ticket>.log`. |
|
|
368
392
|
| 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. |
|
|
369
|
-
| Wait escalation | A queued request waiting longer than the larger of twice its own estimate and ten minutes is flagged as delayed; running jobs silent for more than five minutes show a quiet-duration hint.
|
|
393
|
+
| Wait escalation | A queued request waiting longer than the larger of twice its own estimate and ten minutes is flagged as delayed; running jobs silent for more than five minutes show a quiet-duration hint. A running job past three times its estimate whose process tree has burned no CPU and printed nothing for ten minutes is flagged `stalled`; only a stalled job whose submitting connection is gone is killed automatically. |
|
|
370
394
|
| Daemon status | `running`, `stopped`, or `unresponsive`: a socket that exists but does not answer within its budget is reported as unresponsive, never as stopped. |
|
|
371
395
|
|
|
372
396
|
### Tickets and long-running requests
|
|
373
397
|
|
|
398
|
+

|
|
399
|
+
|
|
374
400
|
Every request has a durable ticket (`cc-<n>`). Its status, exit code, output
|
|
375
401
|
tail, estimate, and timestamps are stored in SQLite and can be read from later
|
|
376
|
-
sessions.
|
|
377
|
-
|
|
402
|
+
sessions. The ledger keeps only a bounded tail (16 KiB); the run's whole
|
|
403
|
+
combined stdout+stderr goes to `<state dir>/tickets/<ticket>.log` as the daemon
|
|
404
|
+
emits it — for a demultiplexed `check`/`build`/`clippy` that is the rendered
|
|
405
|
+
diagnostics stream, not cargo's JSON — up to `CARGO_HAULER_TICKET_LOG_MAX_BYTES`
|
|
406
|
+
(64 MiB by default; the file then ends with one truncation line). A request
|
|
407
|
+
that attached to an in-flight run shares its leader's log, and the row records
|
|
408
|
+
that path. `hauler result cc-N` names the file and its size, `--json` carries
|
|
409
|
+
it as `request.outputPath`, and `hauler result cc-N --full` (or `hauler_result`
|
|
410
|
+
with `full: true`) renders the log itself — so a red `cargo test` is triaged
|
|
411
|
+
from the ticket's own `failures:` list and panic sections instead of a second
|
|
412
|
+
run. The startup retention pass that prunes old ledger rows removes their logs
|
|
413
|
+
too, along with any log whose row is gone. `hauler exec --bg -- cargo …` and
|
|
414
|
+
`hauler_request` return the ticket immediately. A synchronous request also switches to background mode when a
|
|
378
415
|
*measured* estimate — EWMA history or kache priors, never the cold-start
|
|
379
416
|
default — exceeds the host's shell-tool cap (nine minutes for Claude, ten for
|
|
380
417
|
Codex, fourteen for Cursor; the PATH shim uses `CARGO_HAULER_HOST` when it is
|
|
@@ -383,7 +420,9 @@ whole wait: the work queued ahead in the lane plus the job's own runtime,
|
|
|
383
420
|
which the queued line reports as `wait ~Ns, run ~Ns`. That conversion exits
|
|
384
421
|
`75` (`EX_TEMPFAIL`) with the ticket on stderr, so `cargo build && …` chains
|
|
385
422
|
and scripts cannot mistake "submitted" for "built"; explicit `--bg` keeps exit
|
|
386
|
-
`0`.
|
|
423
|
+
`0`. When the caller's stdout is not a terminal (`cargo test > out.log`), the
|
|
424
|
+
notice adds that the redirect receives no output and to read it with
|
|
425
|
+
`hauler result cc-N --full`. Failed runs feed the estimate history too, so a broken build is not
|
|
387
426
|
re-estimated cold on every retry.
|
|
388
427
|
|
|
389
428
|
A foreground `hauler exec` that receives SIGINT or SIGTERM (Ctrl-C, or a
|
|
@@ -396,6 +435,45 @@ start cargo at all. If the connection drops after the ticket was accepted, the
|
|
|
396
435
|
client prints `connection to daemon lost; ticket cc-N continues — hauler
|
|
397
436
|
result cc-N` and exits `1`; the daemon finishes the ticket on its own.
|
|
398
437
|
|
|
438
|
+
A deadlocked test binary holds its lane for ever at 0% CPU with nothing on
|
|
439
|
+
stdout, and neither the estimate overrun nor the output silence alone can
|
|
440
|
+
tell it from a slow build. The daemon therefore samples the CPU time of every
|
|
441
|
+
running ticket's process tree every 30 seconds (`/proc` on Linux, `ps` on
|
|
442
|
+
macOS; other platforms do not detect stalls). A ticket is flagged `stalled`
|
|
443
|
+
when its elapsed time exceeds `CARGO_HAULER_STALL_ESTIMATE_FACTOR` (3) times
|
|
444
|
+
its estimate, the tree's CPU time has not changed for
|
|
445
|
+
`CARGO_HAULER_STALL_IDLE_MS` (ten minutes), and it printed nothing in that
|
|
446
|
+
window. `hauler status`, `hauler_status`, and the dashboard show `stalled`
|
|
447
|
+
with the idle duration; `hauler result` and `hauler_result` answer `ticket
|
|
448
|
+
looks stalled (no CPU for Nm) — hauler kill cc-N`; `hauler await` heartbeats
|
|
449
|
+
say the same. Riders of a stalled leader report the leader's stall and its
|
|
450
|
+
ticket, since killing a rider only detaches it. A stalled ticket whose
|
|
451
|
+
submitting connection is still open is only flagged. When that connection has
|
|
452
|
+
disconnected (a dead agent shell, a killed hook), the ticket is marked
|
|
453
|
+
orphaned, and once it is also stalled the daemon kills it through the normal
|
|
454
|
+
`hauler kill` path — riders settle or requeue as for any kill — with the
|
|
455
|
+
error `stalled: no CPU for Nm after owner disconnected; killed automatically`.
|
|
456
|
+
`CARGO_HAULER_STALL_AUTO_KILL=0` keeps the flag and never kills. Background
|
|
457
|
+
tickets (`--bg`, `hauler_request`) have no streaming connection and are only
|
|
458
|
+
ever flagged.
|
|
459
|
+
|
|
460
|
+
Tickets do not survive a daemon restart. `hauler daemon restart` (or `stop`
|
|
461
|
+
then `start`) ends every queued or running ticket: the cargo processes die
|
|
462
|
+
with the daemon, and the new daemon's first ledger pass marks each of them
|
|
463
|
+
`killed` with the error `orphaned by daemon restart`, so `hauler result cc-N`
|
|
464
|
+
answers `cc-N killed — orphaned by daemon restart: the daemon stopped while it
|
|
465
|
+
was in flight and does not hand runs over; resubmit if the work is still
|
|
466
|
+
needed` rather than looking like a failure of the command itself. Restart when
|
|
467
|
+
the CLI or plugin was upgraded under a running daemon: every document then
|
|
468
|
+
carries `daemon 0.4.2 ≠ cli 0.4.4 — restart it with \`hauler daemon restart\``,
|
|
469
|
+
and a reply the newer client still cannot read is reported as `daemon is 0.4.2
|
|
470
|
+
(pid N, since 3h ago), this CLI is 0.4.4 — restart it with \`hauler daemon
|
|
471
|
+
restart\`` with the first schema mismatch on a second line — never as a raw
|
|
472
|
+
validation dump. The client schemas default the fields older daemons never
|
|
473
|
+
send (`outputPath`, `after`), so a plain version difference alone does not
|
|
474
|
+
break `status`, `result`, or `await`; finish or `hauler kill` what is in
|
|
475
|
+
flight before restarting if the work matters.
|
|
476
|
+
|
|
399
477
|
The `tool/after` route checks the session's background tickets — `--bg`,
|
|
400
478
|
`hauler_request`, and synchronous requests the client converted to a ticket
|
|
401
479
|
— and, on the first tool call after one finishes, adds its result to the
|
|
@@ -469,10 +547,11 @@ is reported as unavailable and never rejects a request.
|
|
|
469
547
|
|
|
470
548
|
| Variable | Default | Meaning |
|
|
471
549
|
| --- | --- | --- |
|
|
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`,
|
|
550
|
+
| `CARGO_HAULER_STATE_DIR` | Per-user cache directory | Unix socket or Windows named pipe source, SQLite ledger, daemon log, pid lock, `hook-state.json`, `hook-events.jsonl`, and the per-ticket output logs under `tickets/`. No legacy alias. |
|
|
473
551
|
| `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
552
|
| `CARGO_HAULER_MAX_CONCURRENT` | `5` | Global admission permits for Cargo processes across all lanes; an integer >= 1. |
|
|
475
553
|
| `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. |
|
|
554
|
+
| `CARGO_HAULER_JOBSERVER` | `auto` | Machine-wide fifo jobserver for daemon-spawned cargo: `auto` arms it only when the host `make` is 4.4+ (or absent) because older makes reject `--jobserver-auth=fifo:` in build scripts; `fifo` forces it on, `off` disables it (per-run `CARGO_BUILD_JOBS` grants apply instead). |
|
|
476
555
|
| `CARGO_HAULER_LOAD_THRESHOLD` | Disabled | Per-core one-minute load threshold for deferring new admissions. |
|
|
477
556
|
| `CARGO_HAULER_LOAD_MIN` | `2` | Active Cargo processes below which load, CPU PSI, and soft memory pressure do not defer admission. |
|
|
478
557
|
| `CARGO_HAULER_CPU_PRESSURE_THRESHOLD` | `75` | Linux CPU PSI `some avg10` percentage for deferring new admissions; `0` or `off` disables. |
|
|
@@ -487,9 +566,13 @@ is reported as unavailable and never rejects a request.
|
|
|
487
566
|
| `CARGO_HAULER_BATCH` | Enabled | `0`, `false`, `off`, or `no` disables the batch composer. |
|
|
488
567
|
| `CARGO_HAULER_BATCH_WINDOW_MS` | `150` | Delay applied to a batchable lane head so nearby requests can fold; `0` disables. |
|
|
489
568
|
| `CARGO_HAULER_KILL_GRACE_MS` | `8000` | Time between SIGTERM and SIGKILL when the daemon stops a Cargo process. |
|
|
569
|
+
| `CARGO_HAULER_STALL_ESTIMATE_FACTOR` | `3` | A running ticket becomes a stall candidate once its elapsed time exceeds this multiple of its estimate. |
|
|
570
|
+
| `CARGO_HAULER_STALL_IDLE_MS` | `600000` | Window with no process-tree CPU time and no output after which a stall candidate is flagged `stalled`; `0` or `off` disables stall detection. |
|
|
571
|
+
| `CARGO_HAULER_STALL_AUTO_KILL` | Enabled | Kill a stalled ticket automatically once the connection that submitted it has disconnected. `0`, `false`, `off`, or `no` only flags it. |
|
|
490
572
|
| `CARGO_HAULER_STOP_WAIT_MS` | `30000` | Maximum wait for one stop-hook invocation; values above the 7200000 ms await ceiling are clamped. |
|
|
491
573
|
| `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. |
|
|
574
|
+
| `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. Pruned rows take their `tickets/<ticket>.log` files with them. |
|
|
575
|
+
| `CARGO_HAULER_TICKET_LOG_MAX_BYTES` | `67108864` (64 MiB) | Bytes of a leader run's combined output written to `<state dir>/tickets/<ticket>.log` before the log stops with one truncation line; `0` writes no ticket logs (`hauler result` then has only the tail). |
|
|
493
576
|
| `CARGO_HAULER_LOG_LEVEL` | `Info` | Daemon log level. |
|
|
494
577
|
| `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. |
|
|
495
578
|
|
|
@@ -519,7 +602,12 @@ unset, the daemon reads kache's configured local store from
|
|
|
519
602
|
`hauler_await` fail loudly when the daemon is unreachable instead of
|
|
520
603
|
reporting a ticket as not found; `hauler_status`, `hauler_log`, and
|
|
521
604
|
`hauler_last` read the ledger with the daemon marked `stopped` or
|
|
522
|
-
`unresponsive`.
|
|
605
|
+
`unresponsive`. A daemon whose reply the client cannot read fails as a
|
|
606
|
+
version difference naming both versions and `hauler daemon restart`.
|
|
607
|
+
- The state directory is not migrated between installs. Every rendered
|
|
608
|
+
document names the one in use (`state dir …` in the header; `stateRoot` in
|
|
609
|
+
`--json`), so a `CARGO_HAULER_STATE_DIR` change is visible on the next
|
|
610
|
+
command rather than discovered from an empty ledger.
|
|
523
611
|
- Test sharing uses identity attachment or batch folding, never coverage.
|
|
524
612
|
Folded `test` and `nextest` requests receive the composite output and exit
|
|
525
613
|
code, so a failure may come from another package in the batch.
|