Repository navigation
chore: re-pin agent-bundle preview d30d9acb6; hauler dashboard uses spawnServeApp - #122
Merged
Merged
Conversation
🦋 Changeset detectedLatest commit: 2190c2c The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
ScriptedAlchemy
added a commit
that referenced
this pull request
Sep 5, 2026
ScriptedAlchemy
added a commit
that referenced
this pull request
Sep 5, 2026
… test the serve-app adapter with an injected spawn Reviewer findings on #122. After the ready line the helper reports a refused SIGTERM only through close(), so an abort whose stop failed left closed pending for ever; serveDashboard now closes on abort itself and turns that stop-failed into the result (with the pid to kill by hand). The route's logic moved to src/lib/dashboard-serve.ts, which the route imports, so the option mapping, every ServeAppCommandError kind, and the abort paths have unit tests.
ScriptedAlchemy
added a commit
that referenced
this pull request
Sep 5, 2026
… close() after it Second-pass finding on #122: passing the request signal to the helper and also calling close() on abort sent the server two SIGTERMs, which can bypass a one-shot graceful handler. The helper now gets a private signal that follows the request only until readiness; after it a single close() stops the server and surfaces stop-failed. The test fake honours options.signal like the real helper and asserts exactly one stop per scenario. Changeset lists every surfaced failure kind.
…pawnServeApp The pkg.pr.new preview of agent-bundle main d30d9acb6bd106762bb975ac58d6bf437c176091 (#588; includes #582 `agent-bundle/serve-app-command` + AB4837, #575 Rslib 1.0 / Rsbuild 2.2, #565 serve-app binds the host's own opening call, #585 AB4770–AB4772). src/cli/dashboard.ts: `hauler dashboard` calls `spawnServeApp` from `agent-bundle/serve-app-command` (agent-bundle#558) instead of its own child-process plumbing. The helper now owns resolving the installed framework CLI (was `frameworkCliPath`), spawning `agent-bundle serve-app` (was `serve`), parsing the ready line (was the `servedUrl` regex), relaying the child's stdout to stderr, and turning the request signal into SIGTERM; its typed `ServeAppCommandError` becomes the command's failure message. What stays is `locateProject` — where this install keeps its artifact — since the helper takes `root`/`artifact` as inputs. Flags, the `hauler_status` opening call, foreground-until-Ctrl-C, and the checkout-only scope are unchanged; the routed bin still never imports the compiler (AB4837), and the built bins inline the helper (no bare specifier, AB6005 clean including the package build's dist). Verified: validate, build (no new diagnostics), typecheck, diagnostics, tests/dashboard.test.ts + tests/version-consistency.test.ts (104) and tests/route-unit/routes.test.ts (7). Smoke: `hauler dashboard --no-open --port 0` printed `MCP App hauler/dashboard at http://127.0.0.1:40577/ (tool hauler_status; Ctrl-C stops the server)`; curl of that URL returned HTTP 200 text/html (`<title>hauler/dashboard</title>`); SIGINT ended the whole tree (`Aborted.`).
… test the serve-app adapter with an injected spawn Reviewer findings on #122. After the ready line the helper reports a refused SIGTERM only through close(), so an abort whose stop failed left closed pending for ever; serveDashboard now closes on abort itself and turns that stop-failed into the result (with the pid to kill by hand). The route's logic moved to src/lib/dashboard-serve.ts, which the route imports, so the option mapping, every ServeAppCommandError kind, and the abort paths have unit tests.
… close() after it Second-pass finding on #122: passing the request signal to the helper and also calling close() on abort sent the server two SIGTERMs, which can bypass a one-shot graceful handler. The helper now gets a private signal that follows the request only until readiness; after it a single close() stops the server and surfaces stop-failed. The test fake honours options.signal like the real helper and asserts exactly one stop per scenario. Changeset lists every surfaced failure kind.
…ing; refused stop) and document why no post-ready window exists
ScriptedAlchemy
force-pushed
the
feat/serve-app-command
branch
from
September 5, 2026 06:59
d094382 to
2190c2c
Compare
7 tasks
Merged
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adopts
spawnServeAppfromagent-bundle/serve-app-commandforhauler dashboardand re-pins theagent-bundle/@agent-bundle/runtimepreview.Pin
cd0b4a62c→d30d9acb6(agent-bundlemainhead): includes #582 (agent-bundle/serve-app-command,AB4837), #588 (packagedist/held toAB6005), #575 (Rslib 1.0 / Rsbuild 2.2), #565 (serve-app binds the host's own opening call), #585 (AB4770–AB4772). It does not include #578 (composite plugin root; still open), so nothing aboutartifact/<host>layout, install/receipt paths, or the~/.codex/config.tomlmarketplace source changes here. Same mechanics as #83/#85: URLs inpackage.json,pnpm installregeneratedpnpm-lock.yaml(agent-bundle's own dependency bumps shrink it ~1000 lines), README pin mention.src/cli/dashboard.ts(181 → 90 lines) +src/lib/dashboard-serve.ts(new, 130 lines)Deleted the
node_modulescrawl for the framework manifest (frameworkCliPath), the stdout URL regex (servedUrl), and the spawn/relay/abort→SIGTERM code (serve).spawnServeAppnow owns CLI resolution, spawningagent-bundle serve-app, ready-line parsing, stdout→stderr relay, and signal teardown;ServeAppCommandError(framework-not-installed,artifact-missing,exited-before-ready, …) becomes the command's message plus "In an MCP host, call hauler_status instead". Unchanged:--target/--port/--no-open, the openinghauler_statuscall,call-toolauto-approve, foreground until Ctrl-C, checkout-only scope.locateProjectstays in the route (the helper takesroot/artifactas inputs); the outcome mapping lives inserveDashboard(src/lib/dashboard-serve.ts, imported by the route) with an injectablespawn, so it is unit-tested. After the ready line the helper reports a refused SIGTERM only throughclose(), soserveDashboardcloses on abort itself and turns astop-failedinto the result (exit 1, message names the pid to kill by hand) instead of awaiting aclosedthat never settles. No compiler import in the routed bin (AB4837never fired).Gate
pnpm checkgreen locally (validate, build —#588's distAB6005passed as-is: the helper is inlined intodist/bin/cargo-hauler.jsand all fourartifact/*/bin/cargo-hauler.mjs, no bareagent-bundlespecifier — typecheck, diagnostics,rstest1016, routes 37);tests/dashboard-serve.test.ts(19 cases with a fake that mirrors the helper's signal /close()contract and counts SIGTERMs: option mapping and defaults, everyServeAppCommandErrorkind before ready incl. the child's exit code / signal, self-exit by code and by signal, abort → close, abort →stop-failedsurfaced, pre-aborted signal, unexpected error rethrown). Smoke:node dist/bin/hauler.js dashboard --no-open --port 0→MCP App hauler/dashboard at http://127.0.0.1:40577/ …;curl→ HTTP 200text/html(<title>hauler/dashboard</title>); SIGINT →Aborted., process tree gone in ~0.5 s; the running daemon untouched.Against #107 §4
This sheds four of the six glue responsibilities #107 §4 lists — crawling
node_modulesfor the framework manifest, spawningagent-bundle serve-app, regex-parsing its human stdout, and hand-relaying abort→SIGTERM — into a typed framework contract. Two remain and are structural: locating checkout/artifact by relative paths (locateProject; the helper takes them as inputs) and checkout-only operation (framework-not-installed/artifact-missingstill fire from the npm package or an installed pack). Both disappear only with agent-bundle #564/#594's framework-generated browser command, at which pointsrc/cli/dashboard.tsis deleted wholesale as #107 intends; nothing here entrenches it. When #578 lands, the only piece to revisit islocateProject'sartifact/<host>/binheuristic and the--targetenum — the same rewrite #107 §1 already lists; thespawnServeAppcall passesroot/artifact/targetthrough and needs no undoing.Self-review
Implementation lane: claude-fable-5-1-thinking-max. Reviewer: gpt-5.6-sol-medium (two passes).
Pass 1 (ae87e12) — 0 blockers, 2 should-fix, both fixed in 56f62d2:
dashboard.ts:115post-readystop-failedunobservable,served.closedpending forever —exitOrStopFailureracesclosedagainstclose()on abort and maps the rejection onto the result.src/lib/dashboard-serve.tswith an injectablespawn;tests/dashboard-serve.test.tsadded.Pass 2 (56f62d2) — 0 blockers, 2 should-fix + 1 nit, fixed in 761fa9a:
close()both stopped the child — the helper now gets a private signal that follows the request only untilspawnsettles (untilReady); after the ready lineclose()is the single owner.options.signal—fakeHelperhonours it like the real helper and every scenario asserts the stop count (0 / 1).spawn-failed/aborted/stop-failed— listed.Pass 3 (d094382) — 0 blockers, 1 should-fix, dismissed with a test (2190c2c): "
helper.signal.abortedblindly awaitsclosed; a refused post-ready SIGTERM would hang". The statehelper.signal.aborted && servedis reachable only when the abort came before the ready line, the helper's SIGTERM was accepted (a refusal rejectsspawnwithstop-failed, now tested), and the child printed its ready line while dying — its exit is on the way and aclose()would be the second SIGTERM the previous pass objected to. An abort cannot land between the ready line and the hand-over: the ready line settlesspawnfrom a stdout event and the continuation runs in the following microtasks, before any signal handler or timer. Both pre-ready races are now tests (readyWhileDying,refuseStop); the reasoning is in the code comment.Rebased onto
masterat c5694dc (#121);pnpm checkgreen after the rebase (1050 unit, 37 routes).