Repository navigation
Align cargo-hauler with Agent Bundle meta-framework architecture (#592) and delete framework workarounds as upstream lands #107
Description
Activity
ScriptedAlchemy commented on Sep 5, 2026
Additional upstream item from the same audit: ScriptedAlchemy/agent-bundle#596 tracks collapsing the duplicated CLI/MCP operation modules themselves, not just their schemas.
cargo-hauler currently has parallel pairs such as src/cli/request.tsx + src/mcp/hauler/tools/hauler_request.tsx and src/cli/status.tsx + src/mcp/hauler/tools/hauler_status.tsx. They correctly share domain functions/documents, but the framework still requires two route implementations to get an idiomatic CLI (positionals, short names, flag mapping) and an MCP tool.
Once #596 lands, add to this umbrella's acceptance: request/status/await/result/kill/last/log each have one canonical operation implementation with MCP and CLI projections, while preserving current CLI UX.
ScriptedAlchemy commented on Sep 5, 2026
Deep-dive decomposition filed from this umbrella:
- Make hook-state updates concurrency-safe across parallel host hook processes #110 — make
hook-state.jsonread/modify/write concurrency-safe (app-level correctness, independent of upstream) - Use Agent Bundle route references for the dashboard binding instead of hand-wiring the MCP App URI #111 — use Agent Bundle
appResourceUri('dashboard')for thehauler_status-> dashboard graph edge instead of a shared string - Infer request cwd from Agent Bundle workspace context instead of requiring MCP callers to restate it #112 — infer request cwd from observed Agent Bundle workspace context; explicit cwd becomes an override
- Collapse duplicate MCP/CLI operation routes once Agent Bundle route projections land #113 — collapse duplicate MCP/CLI route implementations after agent-bundle#593/#596
- Delete the custom MCP App bridge and checkout-only dashboard launcher when Agent Bundle browser runtime lands #114 — delete the hand-written MCP App bridge and checkout-only dashboard launcher after agent-bundle#594/#564
- Stop paying a daemon health probe for every rendered route just to populate the global layout #115 — split cheap daemon config from eager health I/O; stop the root layout from forcing a redundant probe on every rendered route
- Evaluate using Agent Bundle notices only as the delivery channel for cargo-hauler-owned completion events #116 — prove whether Agent Bundle durable notices/state can replace cargo-hauler's custom completion cursor/context-injection subsystem
- Replace host-name targeting on event routes with Agent Bundle capability requirements when available #117 — replace host-name event target allow-lists with framework capability requirements after agent-bundle#592
These sharpen the boundary from the umbrella: cargo-hauler keeps cargo scheduling/daemon/ledger semantics; Agent Bundle should own route relationships, observed request identity, projections, execution lifecycle, notice delivery, App transport, browser hosting, and host capability resolution.
ScriptedAlchemy commented on Sep 5, 2026
Boundary correction after the deeper review: #107 should be read as removing framework-boundary workarounds, not migrating cargo-hauler product architecture into Agent Bundle.
Keep cargo-hauler authoritative for the daemon, scheduler, queue/lane/admission policy, process ownership, ticket/ledger state, completion semantics, Cargo parsing, metrics, output handling, folding/dedupe policy, and product-specific persistence/retry behavior.
Agent Bundle should only replace generic plugin plumbing where it already has or is adding a reusable primitive: route/projection duplication, host event normalization, request identity/workspace observation, App transport/browser hosting, capability negotiation, packaging/install, and optional generic notice delivery.
In particular, do not move stop-deny state, completion state, or daemon/ledger state into Agent Bundle merely because src/state.ts exists. Adopt framework state/notices only where they replace generic transport/infrastructure mechanics without taking over cargo-hauler domain authority. cargo-hauler#116 has been narrowed to reflect this.
agent-bundle PR #601 merged as 0d4a37cef764e54acd93b9d6eb780fa61e5e02eb, completing the typed MCP App client and shared bridge prerequisite for §3. Its read-only dashboard dry-run removed 73 lines of local JSON-RPC/pending-request/envelope plumbing (1,984 → 1,911), with a self-contained browser bundle and generated route typing.
Upstream status for the migration, as of agent-bundle main 512ddaac3 (10:10 UTC):
- #555 W1 composite root landed (agent-bundle #578,
62b69c068): one artifact root, host projections chosen bytargets,install/doctorread the root's hostplugin.jsononly. This is theartifact/<host>coupling listed in §1. - #594 typed MCP App client + shared bridge landed (#601,
0d4a37cef): replaces the hand-written JSON-RPC/postMessage transport indashboard. - #595 preflight gates + lazy providers landed (#618,
ef2abbacc): replaces the parallel fast-hook registration model. - #564 first-class
websurface landed (#620,512ddaac3):<plugin> webfrom the installed artifact. Note: that PR removed theagent-bundle/serve-app-commandexport that cargo-hauler chore: re-pin agent-bundle preview d30d9acb6; hauler dashboard uses spawnServeApp #122 adopted forspawnServeApp; the next agent-bundle preview bump must replacesrc/lib/dashboard-serve.tswith the generatedwebsurface rather than keep the spawn helper. - Still open upstream: #604 authoritative manifest v2 (consumers read the root through the manifest — the last piece §1 waits on), #596 CLI projections (#616), #619 compiler evidence.
Plan: one migration PR here after agent-bundle #604 merges, covering §1 (single root), the bridge, the gates, and web in one preview bump, so cargo-hauler moves once.
Landed in #135 (squash bf9d3a4), released as cargo-hauler@0.6.8 via #137, installed on the owner machine for Claude, Codex, and Cursor from the composite root (cargo-hauler-install install <host> --replace; Codex needed codex plugin marketplace remove cargo-hauler-marketplace first because its registration pointed at the pre-composite artifact/codex), daemon restarted, bin/cargo-hauler.mjs web verified from the installed Claude root.
Workaround → replacement:
| Workaround | Replacement |
|---|---|
artifact/<host> knowledge in config comments, README, docs/install.md, AGENTS.md, dashboard SKILL, version/packed/entry-location tests, pluginCachePath regex |
one artifact/ composite root; targets: ['claude','codex','cursor','portable'] selects projections (agent-bundle#578) |
src/cli/dashboard.ts + src/lib/dashboard-serve.ts (spawnServeApp, agent-bundle/serve-app-command) |
web: { apps: [...] } in agent-bundle.config.ts; the installed plugin's own bin/cargo-hauler.mjs web (agent-bundle#620/#628/#635/#646) |
hand-written JSON-RPC/postMessage transport in apps/dashboard.tsx |
createAppClient from agent-bundle/app (agent-bundle#601) |
config-declared fast hooks (hooks.* block, src/hooks/fast-path/*) |
src/events/tool/{before,after}.tsx event routes with before.preflight.ts / after.preflight.ts gates and providers: [] (agent-bundle#618); the unprobed daemon-health state that only the provider path produced is gone |
duplicated statusInputSchema and the src/cli/{status,log,last,await,result,request,kill}.tsx twins of the MCP tools |
one canonical tool each with a colocated <tool>.cli.ts projection (mapInput for request's positionals/--after), surfaceNames(context) for CLI-vs-MCP naming; --help and per-command output snapshotted before/after, unchanged (agent-bundle#616/#593) |
| framework-generated state under cargo-hauler's own dir | framework state root (agent-bundle#640/#642/#647); CARGO_HAULER_STATE_DIR remains the daemon's |
| per-host packed tests | tests/route-unit/packed-install.test.ts: installs Claude/Codex/Cursor into isolated HOMEs, opens the installed MCP server, serves web |
Kept: #95's bounded status summary.
Remaining items — none blocking, no shim written:
tool/afterqueries the daemon twice when tickets finished, because a preflight cannot hand its result to the route. Filed as Event preflight: let the gate hand a value to the rendered route agent-bundle#661; documented inafter.preflight.ts. Cost is one bounded round trip only when tickets finished.- Machine-only: an existing Codex marketplace registration from the
artifact/<host>era has to be removed by hand once before--replacesucceeds (AB7004).
Upstream follow-up: ScriptedAlchemy/agent-bundle#664 merged as d94223a663c5be3fda85f8809285512b5676e634. Event preflights can now return { outcome: 'execute', data }, and the route receives that typed strict-JSON value as props.preflight across standalone/shared-runtime execution. cargo-hauler can now delete the second tool/after daemon query and reuse the preflight result. The superseding agent-bundle main CI run is green: https://github.com/ScriptedAlchemy/agent-bundle/actions/runs/34009548255
Goal
Use cargo-hauler as a reference consumer of Agent Bundle's #592 architecture rather than preserving workarounds that exist only because current framework surfaces are split.
This is an umbrella migration issue. Do not rewrite working behavior before the relevant upstream capability lands.
1. Composite plugin root / distribution
Blocked on
ScriptedAlchemy/agent-bundle#555.Current cargo-hauler still encodes the old per-target artifact model in:
agent-bundle.config.tscomments (artifact/<target>);README.mdanddocs/install.md(artifact/<host>);AGENTS.md;src/cli/dashboard.tspath heuristics;tests/version-consistency.test.ts;artifact/cursor, etc.;files/installation prose.After #555:
artifact/root should contain selected Claude/Codex/Cursor/portable projections;artifact/<host>;agent-bundle.manifest.json/ host manifest pointers instead of hard-coded partition paths;Do not preserve compatibility path logic in cargo-hauler itself; Agent Bundle owns any legacy artifact probing.
2. Delete duplicated CLI schema contracts
Blocked on
ScriptedAlchemy/agent-bundle#593.src/cli/status.tsxre-declaresstatusInputSchemainline because static argv extraction cannot follow the canonical imported schema.tests/schema-compat.test.tsthen pins the copy tosrc/lib/protocol-schemas.ts.Once #593 lands:
Delete compatibility tests whose only purpose is preventing framework-forced schema drift.
Audit the remaining CLI/MCP pairs (
request,result,await,kill,last,log) for the same duplication and share contracts/domain execution wherever the presentation semantics permit it.3. Delete the hand-written MCP App transport/protocol layer
Blocked on
ScriptedAlchemy/agent-bundle#594.src/mcp/hauler/apps/dashboard.tsxcurrently owns a local JSON-RPC client overwindow.parent.postMessage('*'), request ids/timeouts,tools/callenvelopes, structured-result unwrapping, and manyunknownprotocol interfaces that duplicate the route result schemas.Once #594 lands, the App should consume the generated typed route client and keep only dashboard state/view logic.
Desired shape:
No local knowledge of JSON-RPC ids,
structuredContentenvelope shape, parent bridge semantics, or manually mirrored server protocol types.4. Replace checkout-only
hauler dashboardBlocked on
ScriptedAlchemy/agent-bundle#564and #594.src/cli/dashboard.tsis ~framework integration glue:node_modulesfor Agent Bundle's package manifest;agent-bundle serve-app;After #564, delete this implementation and use the framework-generated production browser command/host.
hauler dashboardshould work from the same installed composite plugin root as the MCP server and routed CLI, with no Agent Bundle checkout dependency.The application should configure/expose
hauler/dashboard; Agent Bundle owns serving, browser opening, lifecycle and URL/result protocol.5. Bring fast shell hooks back into the canonical event model
Blocked on
ScriptedAlchemy/agent-bundle#595.cargo-hauler #90 correctly moved high-frequency shell hooks to
hooks.beforeTool.handler/hooks.afterTool.handler: rendered event routes cost ~0.2 s per shell command pair before they could reject unrelated commands.This workaround should remain until #595 can emit a physically cheap preflight gate.
Afterward:
src/events/tool/{before,after}semantics;src/hooks/fast-path/**if no host-native escape hatch remains necessary;6. Provider cost
src/providers/hauler-daemon.tsprobes daemon health on every non-event route request. This is acceptable today but should be revisited once #595 supports lazy provider materialization/declared provider needs.Routes that only need static/help/render information should not probe the daemon merely because the provider exists globally. Keep the probe where domain semantics require a fresh health snapshot.
7. Keep domain/runtime boundaries
Do not move cargo orchestration into Agent Bundle. cargo-hauler should continue to own:
Agent Bundle should own:
Acceptance
artifact/<host>after #555 adoption.hauler dashboardworks from the installed/npm plugin root without a framework checkout after #564.