cargo-hauler 0.8.1 → 0.8.3

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 (41) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +47 -19
  3. package/dist/.claude-plugin/marketplace.json +1 -1
  4. package/dist/.claude-plugin/plugin.json +1 -1
  5. package/dist/.codex-plugin/plugin.json +1 -1
  6. package/dist/.cursor-plugin/plugin.json +1 -1
  7. package/dist/AGENTS.md +6 -5
  8. package/dist/INSTALL.md +1 -1
  9. package/dist/README.md +47 -19
  10. package/dist/agent-bundle.compile-evidence.json +1 -1
  11. package/dist/agent-bundle.manifest.json +1 -1
  12. package/dist/agent-bundle.package-compile-evidence.json +1 -1
  13. package/dist/bin/cargo-hauler-flight.mjs +83 -48
  14. package/dist/bin/cargo-hauler.mjs +126 -91
  15. package/dist/bin/hauler.js +138 -107
  16. package/dist/hooks/event-route-session-start.claude.mjs +2 -2
  17. package/dist/hooks/event-route-session-start.codex.mjs +2 -2
  18. package/dist/hooks/event-route-session-start.cursor.mjs +2 -2
  19. package/dist/hooks/event-route-stop.claude.mjs +2 -2
  20. package/dist/hooks/event-route-stop.codex.mjs +2 -2
  21. package/dist/hooks/event-route-stop.cursor.mjs +2 -2
  22. package/dist/hooks/event-route-tool-after.claude.execute.mjs +2 -2
  23. package/dist/hooks/event-route-tool-after.claude.mjs +9 -9
  24. package/dist/hooks/event-route-tool-after.codex.execute.mjs +2 -2
  25. package/dist/hooks/event-route-tool-after.codex.mjs +9 -9
  26. package/dist/hooks/event-route-tool-after.cursor.execute.mjs +2 -2
  27. package/dist/hooks/event-route-tool-after.cursor.mjs +9 -9
  28. package/dist/hooks/event-route-tool-before.claude.execute.mjs +2 -2
  29. package/dist/hooks/event-route-tool-before.codex.execute.mjs +2 -2
  30. package/dist/hooks/event-route-tool-before.cursor.execute.mjs +2 -2
  31. package/dist/hooks/hooks-flight.mjs +65 -35
  32. package/dist/install.mjs +1 -1
  33. package/dist/mcp/mcp-hauler-2b8242f9-flight.mjs +88 -53
  34. package/dist/mcp/mcp-hauler-2b8242f9.mjs +86 -51
  35. package/dist/mcp-apps/dashboard.html +2 -2
  36. package/dist/package.json +1 -1
  37. package/dist/plugin.json +1 -1
  38. package/dist/scripts/hauler.mjs +138 -107
  39. package/dist/skills/cargo-hauler/SKILL.md +4 -1
  40. package/dist/skills/hauler-dashboard/SKILL.md +1 -1
  41. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 711f6ea: Identity, coverage, and batch/fold now refuse to share when the forwarded caller environment differs, so two `cargo test`/`check` runs that differ only in `OUT` (or any other variable cargo will see) no longer coalesce onto one result (#222). Duration estimates still key on the compile surface — including when the variable arrives via `env OUT=…` — so one-off output paths do not cold-start EWMA. The PATH shim stays in the foreground for non-TTY callers instead of returning exit 75 and running later against torn-down state; `CARGO_HAULER_SHIM_BACKGROUND=1` restores auto-background for scripts that consume tickets (#223).
8
+
9
+ ## 0.8.2
10
+
11
+ ### Patch Changes
12
+
13
+ - 7490839: Internal: the implementation behind the Agent Bundle entrypoints now lives
14
+ under `src/internal/<owner>/` (contracts, cargo, daemon/{runtime,broker,
15
+ scheduling,reporting}, storage, client, operations, host-hooks,
16
+ integrations/kache, platform, shim, ui/{documents,dashboard,shared}, util)
17
+ instead of `daemon/`, `lib/`, `hooks/`, and friends; `tests/` is grouped by
18
+ what a test executes (unit, integration, packaging, acceptance). A move-only
19
+ change: no route, executable, protocol, or scheduling behaviour changed.
20
+ `docs/architecture.md` is the ownership map and walkthrough.
21
+
3
22
  ## 0.8.1
4
23
 
5
24
  ### Patch Changes
package/README.md CHANGED
@@ -378,16 +378,20 @@ too, along with any log whose row is gone. `hauler exec --bg -- cargo …` and
378
378
  `hauler_request` return the ticket immediately. A synchronous request also switches to background mode when a
379
379
  *measured* estimate — EWMA history or kache priors, never the cold-start
380
380
  default — exceeds the host's shell-tool cap (nine minutes for Claude, ten for
381
- Codex, fourteen for Cursor; the PATH shim uses `CARGO_HAULER_HOST` when it is
382
- exported, otherwise the Claude cap). The estimate that is compared is the
383
- whole wait: the work queued ahead in the lane plus the job's own runtime,
384
- which the queued line reports as `wait ~Ns, run ~Ns`. That conversion exits
385
- `75` (`EX_TEMPFAIL`) with the ticket on stderr, so `cargo build && …` chains
386
- and scripts cannot mistake "submitted" for "built"; explicit `--bg` keeps exit
387
- `0`. When the caller's stdout is not a terminal (`cargo test > out.log`), the
388
- notice adds that the redirect receives no output and to read it with
389
- `hauler result cc-N --full`. Failed runs feed the estimate history too, so a broken build is not
390
- re-estimated cold on every retry.
381
+ Codex, fourteen for Cursor). The PATH shim (`--host shim`) is cargo to its
382
+ caller: a non-TTY invocation (a script, `spawnSync`, `make`) waits for the
383
+ ticket to finish and returns cargo's exit code. An interactive shim (stdout
384
+ is a TTY) still auto-backgrounds on the cap, using `CARGO_HAULER_HOST` when
385
+ it is exported, otherwise the Claude cap; `CARGO_HAULER_SHIM_BACKGROUND=1`
386
+ restores that for scripts that consume tickets themselves. The estimate that
387
+ is compared is the whole wait: the work queued ahead in the lane plus the
388
+ job's own runtime, which the queued line reports as `wait ~Ns, run ~Ns`. That
389
+ conversion exits `75` (`EX_TEMPFAIL`) with the ticket on stderr, so
390
+ `cargo build && …` chains and scripts cannot mistake "submitted" for "built";
391
+ explicit `--bg` keeps exit `0`. When the caller's stdout is not a terminal
392
+ (`cargo test > out.log`), the notice adds that the redirect receives no
393
+ output and to read it with `hauler result cc-N --full`. Failed runs feed the
394
+ estimate history too, so a broken build is not re-estimated cold on every retry.
391
395
 
392
396
  A foreground `hauler exec` that receives SIGINT or SIGTERM (Ctrl-C, or a
393
397
  `timeout N …` wrapper) asks the daemon to kill its ticket, waits for the
@@ -674,7 +678,8 @@ Per-host notes and hook timeouts are in [docs/install.md](docs/install.md).
674
678
  | `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. |
675
679
  | `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). |
676
680
  | `CARGO_HAULER_LOG_LEVEL` | `Info` | Daemon log level. |
677
- | `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. |
681
+ | `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 when auto-background is allowed. |
682
+ | `CARGO_HAULER_SHIM_BACKGROUND` | Unset | `1` / `true` / `on` / `yes` lets a non-TTY PATH shim auto-background over the host cap (exit 75). Without it, a shim that is not a TTY waits for cargo's exit. |
678
683
 
679
684
  A numeric value that does not parse or falls outside its range is reported
680
685
  as a warning (daemon log, or stderr for hand-run commands) and the default
@@ -787,7 +792,6 @@ argv parser, or string-concatenated Markdown; the `src/` tree is the app.
787
792
  src/
788
793
  layout.tsx the hauler shell around every rendered route
789
794
  providers/hauler-daemon.ts request-scoped daemon configuration
790
- components/ typed components over pure view-models
791
795
  mcp/hauler/tools/*.tsx hauler_status, _dashboard, _log, _last, _await, _result, _request, _kill
792
796
  mcp/hauler/tools/*.cli.ts each tool's `hauler <command>` projection (flags, positionals)
793
797
  mcp/hauler/apps/dashboard.tsx the MCP App (ui://cargo-hauler/dashboard.html)
@@ -796,9 +800,26 @@ src/
796
800
  events/tool/{before,after}.tsx the shell hook routes, gated by *.preflight.ts
797
801
  skills/cargo-hauler/SKILL.md, skills/hauler-dashboard/SKILL.tsx
798
802
  scripts/hauler.ts the `hauler` process entry hooks rewrite cargo to
799
- daemon/, client/, hooks/, shim/, lib/ the broker and its libraries
803
+ internal/ the implementation the entrypoints import, by owner
804
+ contracts/ wire protocol, tool schemas, wire version
805
+ cargo/ argv, intent, workspace, topology, execution/
806
+ daemon/ composition (main, config), runtime/, broker/, scheduling/, reporting/
807
+ storage/ the SQLite ledger and per-ticket output logs
808
+ client/ socket clients: control, exec, tickets, ensure-daemon
809
+ operations/ what the routes call: tickets, status, inspection
810
+ host-hooks/ the shell/session hook handlers and their small RPC client
811
+ integrations/kache/ kache status and store pressure
812
+ platform/ state and socket paths, private-file policy, executable location
813
+ shim/ the PATH shim installer
814
+ ui/documents/, ui/dashboard/, ui/shared/ agent documents, the browser App, shared formatters
815
+ util/ guards, ids, text, ANSI
800
816
  ```
801
817
 
818
+ Everything above `internal/` is discovered by the framework's conventions;
819
+ everything under it is ordinary imported code. [docs/architecture.md](docs/architecture.md)
820
+ is the ownership map and walks the three main paths (submitting cargo, reading
821
+ status, processing a shell hook) file by file.
822
+
802
823
  #### The shell (`src/layout.tsx`)
803
824
 
804
825
  Every rendered route — MCP tool, CLI command, rendered script — composes
@@ -826,7 +847,7 @@ addition: each re-exports a `preflight` (`before.preflight.ts`,
826
847
  `hooks/event-route-tool-before.<host>.mjs`, a few hundred KB with no React,
827
848
  Flight worker, or Effect — and runs before the rendered route
828
849
  (`*.execute.mjs`) is loaded. The gate decides on the raw command
829
- (`src/hooks/tokens.ts`; `session-ping.ts` for the one bounded completion ping
850
+ (`src/internal/host-hooks/tokens.ts`; `session-ping.ts` for the one bounded completion ping
830
851
  after a tool ran): `continue` for the shell calls that name neither cargo nor
831
852
  hauler, `execute` for the rest. Both routes declare `providers: []`, so
832
853
  neither mounts the daemon-config provider; the rendered route calls
@@ -843,7 +864,7 @@ socket, and ledger paths); active health and status I/O belongs to the operation
843
864
  that needs it. Routes read the config through `requestDaemonConfig(context)`,
844
865
  and tests inject a fixture through the harness `context.providers` seam.
845
866
 
846
- #### Components (`src/components/`)
867
+ #### Components (`src/internal/ui/documents/`)
847
868
 
848
869
  Components render view-models and nothing else. The models are pure functions
849
870
  in `view-models.ts`, so the MCP document, the CLI Markdown, and a test
@@ -871,7 +892,7 @@ assertion share one derivation.
871
892
  same operation render the same document with different command spellings
872
893
  (`surface.ts`).
873
894
 
874
- #### Streaming (`src/components/streaming.tsx`)
895
+ #### Streaming (`src/internal/ui/documents/streaming.tsx`)
875
896
 
876
897
  `hauler_await` and `hauler_log` are progressive documents. Each is a
877
898
  valueless `Agent.Result` container around one `Suspense` boundary:
@@ -944,7 +965,7 @@ with the wait split by cause: *lane-bound* (a same-lane leader was still
944
965
  compiling — before its `Finished` line or exit), *permit-bound* (every
945
966
  admission permit was held and no same-lane compile was to blame), and *other*
946
967
  (admission holds, `--after` prerequisites, scheduling latency). The
947
- classification is a pure sweep over ledger rows (`src/daemon/wait-split.ts`)
968
+ classification is a pure sweep over ledger rows (`src/internal/daemon/reporting/wait-split.ts`)
948
969
  run once per status refresh against the daemon's current permit count, which
949
970
  the tile states; runs admitted under an earlier cap are classified against
950
971
  today's. With `buildFinishedAtMs` on the row, the by-command split adds
@@ -957,12 +978,19 @@ the lane time the execution-phase hand-back released.
957
978
  pnpm run check # validate + build + typecheck + Effect diagnostics + rstest + route tests
958
979
  ```
959
980
 
981
+ `tests/` is grouped by what a test executes: `unit/<subsystem>/` is scoped to
982
+ one owner under `src/internal/`; `integration/` runs a real broker
983
+ in-process through `tests/support/harness.ts`; `packaging/` reads the built
984
+ `artifact/` or spawns the package entries; `acceptance/` drives real `cargo`
985
+ against the workspaces under `evals/fixtures/`; `route-unit/` and
986
+ `browser-app/` are the framework-level suites below.
987
+
960
988
  `tests/route-unit/` renders the app through the framework compiler with no
961
989
  artifact build, at the harness proof levels:
962
990
 
963
991
  | Level | Suite | What it proves |
964
992
  | --- | --- | --- |
965
- | route-unit | `routes`, `layout`, `streaming`, `events` | documents, shell metadata, Suspense fallbacks and settled values, lineage attribution, event decisions (the shell routes' preflight gates are unit-tested in `tests/event-preflight.test.ts` and against their compiled entries in `tests/hooks-simulate.test.ts`) |
993
+ | route-unit | `routes`, `layout`, `streaming`, `events` | documents, shell metadata, Suspense fallbacks and settled values, lineage attribution, event decisions (the shell routes' preflight gates are unit-tested in `tests/integration/event-preflight.test.ts` and against their compiled entries in `tests/integration/hooks-simulate.test.ts`) |
966
994
  | cli-dispatch | `cli-dispatch`, `layout` | argv through the routed CLI shell; Markdown wrapped by the shell, `--json` bare |
967
995
  | script-dispatch | `script-dispatch` | the `hauler` entry through its `main` envelope as its own process |
968
996
  | mcp-in-memory | `mcp-surface`, `layout` | tool names, `outputSchema`, the dashboard resource link, `_meta.hauler`, and a live fixture broker over the in-memory transport |
@@ -971,7 +999,7 @@ artifact build, at the harness proof levels:
971
999
  | workbench-surface | `workbench-surface` | what `agent-bundle dev` would show: catalog, provider, lifecycles per host, counts |
972
1000
 
973
1001
  Daemon-backed cases run a real broker in-process with a fake `cargo`
974
- (`tests/harness.ts`) and reach it either through the `haulerDaemon` provider
1002
+ (`tests/support/harness.ts`) and reach it either through the `haulerDaemon` provider
975
1003
  seam or through `CARGO_HAULER_STATE_DIR`.
976
1004
 
977
1005
  ### Development
@@ -1 +1 @@
1
- {"description":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","name":"cargo-hauler-marketplace","owner":{"name":"cargo-hauler"},"plugins":[{"description":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","keywords":["cargo","rust","agent","mcp","claude-code","codex","cursor","build-orchestration"],"license":"MIT","name":"cargo-hauler","repository":"https://github.com/ScriptedAlchemy/cargo-hauler","source":"./","version":"0.8.1"}]}
1
+ {"description":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","name":"cargo-hauler-marketplace","owner":{"name":"cargo-hauler"},"plugins":[{"description":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","keywords":["cargo","rust","agent","mcp","claude-code","codex","cursor","build-orchestration"],"license":"MIT","name":"cargo-hauler","repository":"https://github.com/ScriptedAlchemy/cargo-hauler","source":"./","version":"0.8.3"}]}
@@ -1 +1 @@
1
- {"author":{"name":"cargo-hauler"},"description":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","name":"cargo-hauler","version":"0.8.1"}
1
+ {"author":{"name":"cargo-hauler"},"description":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","name":"cargo-hauler","version":"0.8.3"}
@@ -1 +1 @@
1
- {"author":{"name":"cargo-hauler"},"description":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","hooks":"./.codex-plugin/hooks.json","interface":{"capabilities":["mcp","hooks","skills"],"category":"Productivity","defaultPrompt":["Help me use cargo-hauler."],"developerName":"cargo-hauler","displayName":"cargo-hauler","longDescription":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","shortDescription":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks."},"keywords":["cargo","rust","agent","mcp","claude-code","codex","cursor","build-orchestration"],"license":"MIT","mcpServers":"./.codex-plugin/mcp.json","name":"cargo-hauler","repository":"https://github.com/ScriptedAlchemy/cargo-hauler","skills":"./skills/","version":"0.8.1"}
1
+ {"author":{"name":"cargo-hauler"},"description":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","hooks":"./.codex-plugin/hooks.json","interface":{"capabilities":["mcp","hooks","skills"],"category":"Productivity","defaultPrompt":["Help me use cargo-hauler."],"developerName":"cargo-hauler","displayName":"cargo-hauler","longDescription":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","shortDescription":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks."},"keywords":["cargo","rust","agent","mcp","claude-code","codex","cursor","build-orchestration"],"license":"MIT","mcpServers":"./.codex-plugin/mcp.json","name":"cargo-hauler","repository":"https://github.com/ScriptedAlchemy/cargo-hauler","skills":"./skills/","version":"0.8.3"}
@@ -1 +1 @@
1
- {"description":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","displayName":"cargo-hauler","hooks":"./.cursor-plugin/hooks.json","keywords":["cargo","rust","agent","mcp","claude-code","codex","cursor","build-orchestration"],"license":"MIT","mcpServers":"./.cursor-plugin/mcp.json","name":"cargo-hauler","repository":"https://github.com/ScriptedAlchemy/cargo-hauler","skills":"./skills/","version":"0.8.1"}
1
+ {"description":"Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.","displayName":"cargo-hauler","hooks":"./.cursor-plugin/hooks.json","keywords":["cargo","rust","agent","mcp","claude-code","codex","cursor","build-orchestration"],"license":"MIT","mcpServers":"./.cursor-plugin/mcp.json","name":"cargo-hauler","repository":"https://github.com/ScriptedAlchemy/cargo-hauler","skills":"./skills/","version":"0.8.3"}
package/dist/AGENTS.md CHANGED
@@ -11,14 +11,15 @@
11
11
  diagnostics, `rstest`, the route-unit suite, and browser App tests. Run it
12
12
  before claiming a change is done.
13
13
  - The plugin surface is an agent-bundle application: `src/layout.tsx` (the
14
- shell), `src/providers/hauler-daemon.ts` (daemon connection), `src/components`
15
- (typed components over `view-models.ts`), `src/mcp/hauler/tools` and
14
+ shell), `src/providers/hauler-daemon.ts` (daemon connection),
15
+ `src/internal/ui/documents` (typed components over `view-models.ts`), `src/mcp/hauler/tools` and
16
16
  `src/mcp/hauler/apps` (MCP; each tool's `<tool>.cli.ts` is its `hauler`
17
17
  command), `src/events` (hook routes; `tool/before.preflight.ts` and
18
18
  `tool/after.preflight.ts` decide on the raw command before the route
19
- loads — keep them free of React and Effect), `src/hooks` (the handlers
20
- the routes call), `src/cli/daemon.ts`, `src/scripts/hauler.ts` (process entry),
21
- `src/skills`. The README's tour is the map; do not reintroduce a
19
+ loads — keep them free of React and Effect), `src/internal/host-hooks` (the
20
+ handlers the routes call), `src/cli/daemon.ts`, `src/scripts/hauler.ts` (process
21
+ entry), `src/skills`. Everything else lives under `src/internal/<owner>/`
22
+ (`docs/architecture.md` is the ownership map); do not reintroduce a
22
23
  hand-written server, argv parser, or string-concatenated documents — add a
23
24
  component and a view-model.
24
25
  - Documents must stay honest: a daemon the probe could not reach renders as
package/dist/INSTALL.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Coalesce, schedule, and stream cargo so concurrent agent sessions share compiles instead of fighting locks.
4
4
 
5
- Version: `0.8.1`
5
+ Version: `0.8.3`
6
6
 
7
7
  Run these commands from this bundle directory. The bundle is self-contained: every command below is
8
8
  a host command or the bundled installer, and nothing requires the `agent-bundle` CLI. Where that CLI is
package/dist/README.md CHANGED
@@ -378,16 +378,20 @@ too, along with any log whose row is gone. `hauler exec --bg -- cargo …` and
378
378
  `hauler_request` return the ticket immediately. A synchronous request also switches to background mode when a
379
379
  *measured* estimate — EWMA history or kache priors, never the cold-start
380
380
  default — exceeds the host's shell-tool cap (nine minutes for Claude, ten for
381
- Codex, fourteen for Cursor; the PATH shim uses `CARGO_HAULER_HOST` when it is
382
- exported, otherwise the Claude cap). The estimate that is compared is the
383
- whole wait: the work queued ahead in the lane plus the job's own runtime,
384
- which the queued line reports as `wait ~Ns, run ~Ns`. That conversion exits
385
- `75` (`EX_TEMPFAIL`) with the ticket on stderr, so `cargo build && …` chains
386
- and scripts cannot mistake "submitted" for "built"; explicit `--bg` keeps exit
387
- `0`. When the caller's stdout is not a terminal (`cargo test > out.log`), the
388
- notice adds that the redirect receives no output and to read it with
389
- `hauler result cc-N --full`. Failed runs feed the estimate history too, so a broken build is not
390
- re-estimated cold on every retry.
381
+ Codex, fourteen for Cursor). The PATH shim (`--host shim`) is cargo to its
382
+ caller: a non-TTY invocation (a script, `spawnSync`, `make`) waits for the
383
+ ticket to finish and returns cargo's exit code. An interactive shim (stdout
384
+ is a TTY) still auto-backgrounds on the cap, using `CARGO_HAULER_HOST` when
385
+ it is exported, otherwise the Claude cap; `CARGO_HAULER_SHIM_BACKGROUND=1`
386
+ restores that for scripts that consume tickets themselves. The estimate that
387
+ is compared is the whole wait: the work queued ahead in the lane plus the
388
+ job's own runtime, which the queued line reports as `wait ~Ns, run ~Ns`. That
389
+ conversion exits `75` (`EX_TEMPFAIL`) with the ticket on stderr, so
390
+ `cargo build && …` chains and scripts cannot mistake "submitted" for "built";
391
+ explicit `--bg` keeps exit `0`. When the caller's stdout is not a terminal
392
+ (`cargo test > out.log`), the notice adds that the redirect receives no
393
+ output and to read it with `hauler result cc-N --full`. Failed runs feed the
394
+ estimate history too, so a broken build is not re-estimated cold on every retry.
391
395
 
392
396
  A foreground `hauler exec` that receives SIGINT or SIGTERM (Ctrl-C, or a
393
397
  `timeout N …` wrapper) asks the daemon to kill its ticket, waits for the
@@ -674,7 +678,8 @@ Per-host notes and hook timeouts are in [docs/install.md](docs/install.md).
674
678
  | `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. |
675
679
  | `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). |
676
680
  | `CARGO_HAULER_LOG_LEVEL` | `Info` | Daemon log level. |
677
- | `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. |
681
+ | `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 when auto-background is allowed. |
682
+ | `CARGO_HAULER_SHIM_BACKGROUND` | Unset | `1` / `true` / `on` / `yes` lets a non-TTY PATH shim auto-background over the host cap (exit 75). Without it, a shim that is not a TTY waits for cargo's exit. |
678
683
 
679
684
  A numeric value that does not parse or falls outside its range is reported
680
685
  as a warning (daemon log, or stderr for hand-run commands) and the default
@@ -787,7 +792,6 @@ argv parser, or string-concatenated Markdown; the `src/` tree is the app.
787
792
  src/
788
793
  layout.tsx the hauler shell around every rendered route
789
794
  providers/hauler-daemon.ts request-scoped daemon configuration
790
- components/ typed components over pure view-models
791
795
  mcp/hauler/tools/*.tsx hauler_status, _dashboard, _log, _last, _await, _result, _request, _kill
792
796
  mcp/hauler/tools/*.cli.ts each tool's `hauler <command>` projection (flags, positionals)
793
797
  mcp/hauler/apps/dashboard.tsx the MCP App (ui://cargo-hauler/dashboard.html)
@@ -796,9 +800,26 @@ src/
796
800
  events/tool/{before,after}.tsx the shell hook routes, gated by *.preflight.ts
797
801
  skills/cargo-hauler/SKILL.md, skills/hauler-dashboard/SKILL.tsx
798
802
  scripts/hauler.ts the `hauler` process entry hooks rewrite cargo to
799
- daemon/, client/, hooks/, shim/, lib/ the broker and its libraries
803
+ internal/ the implementation the entrypoints import, by owner
804
+ contracts/ wire protocol, tool schemas, wire version
805
+ cargo/ argv, intent, workspace, topology, execution/
806
+ daemon/ composition (main, config), runtime/, broker/, scheduling/, reporting/
807
+ storage/ the SQLite ledger and per-ticket output logs
808
+ client/ socket clients: control, exec, tickets, ensure-daemon
809
+ operations/ what the routes call: tickets, status, inspection
810
+ host-hooks/ the shell/session hook handlers and their small RPC client
811
+ integrations/kache/ kache status and store pressure
812
+ platform/ state and socket paths, private-file policy, executable location
813
+ shim/ the PATH shim installer
814
+ ui/documents/, ui/dashboard/, ui/shared/ agent documents, the browser App, shared formatters
815
+ util/ guards, ids, text, ANSI
800
816
  ```
801
817
 
818
+ Everything above `internal/` is discovered by the framework's conventions;
819
+ everything under it is ordinary imported code. [docs/architecture.md](docs/architecture.md)
820
+ is the ownership map and walks the three main paths (submitting cargo, reading
821
+ status, processing a shell hook) file by file.
822
+
802
823
  #### The shell (`src/layout.tsx`)
803
824
 
804
825
  Every rendered route — MCP tool, CLI command, rendered script — composes
@@ -826,7 +847,7 @@ addition: each re-exports a `preflight` (`before.preflight.ts`,
826
847
  `hooks/event-route-tool-before.<host>.mjs`, a few hundred KB with no React,
827
848
  Flight worker, or Effect — and runs before the rendered route
828
849
  (`*.execute.mjs`) is loaded. The gate decides on the raw command
829
- (`src/hooks/tokens.ts`; `session-ping.ts` for the one bounded completion ping
850
+ (`src/internal/host-hooks/tokens.ts`; `session-ping.ts` for the one bounded completion ping
830
851
  after a tool ran): `continue` for the shell calls that name neither cargo nor
831
852
  hauler, `execute` for the rest. Both routes declare `providers: []`, so
832
853
  neither mounts the daemon-config provider; the rendered route calls
@@ -843,7 +864,7 @@ socket, and ledger paths); active health and status I/O belongs to the operation
843
864
  that needs it. Routes read the config through `requestDaemonConfig(context)`,
844
865
  and tests inject a fixture through the harness `context.providers` seam.
845
866
 
846
- #### Components (`src/components/`)
867
+ #### Components (`src/internal/ui/documents/`)
847
868
 
848
869
  Components render view-models and nothing else. The models are pure functions
849
870
  in `view-models.ts`, so the MCP document, the CLI Markdown, and a test
@@ -871,7 +892,7 @@ assertion share one derivation.
871
892
  same operation render the same document with different command spellings
872
893
  (`surface.ts`).
873
894
 
874
- #### Streaming (`src/components/streaming.tsx`)
895
+ #### Streaming (`src/internal/ui/documents/streaming.tsx`)
875
896
 
876
897
  `hauler_await` and `hauler_log` are progressive documents. Each is a
877
898
  valueless `Agent.Result` container around one `Suspense` boundary:
@@ -944,7 +965,7 @@ with the wait split by cause: *lane-bound* (a same-lane leader was still
944
965
  compiling — before its `Finished` line or exit), *permit-bound* (every
945
966
  admission permit was held and no same-lane compile was to blame), and *other*
946
967
  (admission holds, `--after` prerequisites, scheduling latency). The
947
- classification is a pure sweep over ledger rows (`src/daemon/wait-split.ts`)
968
+ classification is a pure sweep over ledger rows (`src/internal/daemon/reporting/wait-split.ts`)
948
969
  run once per status refresh against the daemon's current permit count, which
949
970
  the tile states; runs admitted under an earlier cap are classified against
950
971
  today's. With `buildFinishedAtMs` on the row, the by-command split adds
@@ -957,12 +978,19 @@ the lane time the execution-phase hand-back released.
957
978
  pnpm run check # validate + build + typecheck + Effect diagnostics + rstest + route tests
958
979
  ```
959
980
 
981
+ `tests/` is grouped by what a test executes: `unit/<subsystem>/` is scoped to
982
+ one owner under `src/internal/`; `integration/` runs a real broker
983
+ in-process through `tests/support/harness.ts`; `packaging/` reads the built
984
+ `artifact/` or spawns the package entries; `acceptance/` drives real `cargo`
985
+ against the workspaces under `evals/fixtures/`; `route-unit/` and
986
+ `browser-app/` are the framework-level suites below.
987
+
960
988
  `tests/route-unit/` renders the app through the framework compiler with no
961
989
  artifact build, at the harness proof levels:
962
990
 
963
991
  | Level | Suite | What it proves |
964
992
  | --- | --- | --- |
965
- | route-unit | `routes`, `layout`, `streaming`, `events` | documents, shell metadata, Suspense fallbacks and settled values, lineage attribution, event decisions (the shell routes' preflight gates are unit-tested in `tests/event-preflight.test.ts` and against their compiled entries in `tests/hooks-simulate.test.ts`) |
993
+ | route-unit | `routes`, `layout`, `streaming`, `events` | documents, shell metadata, Suspense fallbacks and settled values, lineage attribution, event decisions (the shell routes' preflight gates are unit-tested in `tests/integration/event-preflight.test.ts` and against their compiled entries in `tests/integration/hooks-simulate.test.ts`) |
966
994
  | cli-dispatch | `cli-dispatch`, `layout` | argv through the routed CLI shell; Markdown wrapped by the shell, `--json` bare |
967
995
  | script-dispatch | `script-dispatch` | the `hauler` entry through its `main` envelope as its own process |
968
996
  | mcp-in-memory | `mcp-surface`, `layout` | tool names, `outputSchema`, the dashboard resource link, `_meta.hauler`, and a live fixture broker over the in-memory transport |
@@ -971,7 +999,7 @@ artifact build, at the harness proof levels:
971
999
  | workbench-surface | `workbench-surface` | what `agent-bundle dev` would show: catalog, provider, lifecycles per host, counts |
972
1000
 
973
1001
  Daemon-backed cases run a real broker in-process with a fake `cargo`
974
- (`tests/harness.ts`) and reach it either through the `haulerDaemon` provider
1002
+ (`tests/support/harness.ts`) and reach it either through the `haulerDaemon` provider
975
1003
  seam or through `CARGO_HAULER_STATE_DIR`.
976
1004
 
977
1005
  ### Development