✅ test: launch the CLI through the host runtime - #186
Merged
Conversation
PR #186: ✅ test: launch the CLI through the host runtime14 files, +72 / -47 Scope🟡 PR mixes config and source changes. Structural✅ No structural bloat detected. Slop✅ Slop indicators look low. Static Analysis✅ Oxlint found no issues. CorrectnessNo extraneous code patterns detected. |
4 tasks
Ten suites shelled out to the CLI with a hardcoded `deno run --allow-all packages/cli/src/deno.ts`. That pinned them to one runtime and hid whether the CLI works under the others — the npm package runs under Node, and the published bin is what users get. `@executablemd/test-support/launch` builds the command for whichever runtime is running the tests, using the entrypoint that runtime installs its `command` and `compile` providers from. It returns a command, never a process: seven suites spawn it and each consumes the process differently — `.expect()`, `.join()`, manual stdout streaming under four timeboxes, and one long-lived JSON-RPC peer — while three never spawn it at all, handing a base array to `API.Env.command` for the test-agent provider to launch. Detection belongs here rather than being tolerated: packages/test-support is the host-adapter boundary for tests, which rule 12 already exempts (#188). Node starts a fresh tsx process rather than reusing the running one, so a CLI subprocess does not inherit `--test` or the runner's loaders, and naming the tsconfig on the command line means no suite has to alter its environment. Suite-specific environments, timeouts and process handling are untouched: agent-cli isolates HOME, props-cli allowlists so an inherited XMD_PROPS_* cannot corrupt its precedence assertions, and the rest inherit deliberately. worker-lifecycle passed its whole command line as one interpolated string and now uses command + arguments form. XC4 asserted "the Deno entrypoint relaunches a worker"; it exercises whichever runtime is running, and says so. This makes the ten suites runtime-portable. Adding them to the derived Node and Bun scope is #144. Ran explicitly: 73 tests, 0 failures under Deno, Node and Bun.
taras
force-pushed
the
test/runtime-launcher
branch
from
July 28, 2026 14:14
46d5614 to
839ca3a
Compare
The module JSDoc on launch.ts restated what the package name, the exported functions, and AGENTS.md rule 12 already say. The two comments that carry rationale a reader cannot infer stay: why paths come from import.meta.url, and why Node starts a fresh tsx process. command.test.ts said it inherited the environment "so the child resolves the same Deno install", which stopped being the reason when the suite started launching whichever runtime runs it. What the inheritance is actually for is PATH, so the Node launcher can find tsx.
4 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.
Why
Preparation for #144. Ten suites shell out to the CLI with a hardcoded
deno run --allow-all packages/cli/src/deno.ts. That pins them to one runtimeand hides whether the CLI works under the others — the npm package runs under
Node, and the published bin is what users get.
This PR makes those ten suites runtime-portable. Adding them to the derived
Node and Bun scope is #144 itself.
What changes
Before: every CLI-launching suite spawned Deno, whatever runtime was running
the tests.
After: each launches the entrypoint belonging to the host runtime. Verified:
73 tests, 0 failures under Deno, Node and Bun.
How it works
@executablemd/test-support/launchbuilds the command, using the entrypointthat runtime installs its
commandandcompileproviders from (#184):[execPath, "run", "--allow-all", <root>/packages/cli/src/deno.ts][execPath, <root>/packages/cli/src/bun.ts]["tsx", "--tsconfig", <root>/tsconfig.node.json, <root>/packages/cli/src/node.ts]<root>comes fromimport.meta.url, not the working directory —props-cliand
commandrun the CLI from temp directories.It returns a command, never a process. Two shapes need it. Seven suites
spawn, each consuming the process differently:
.expect(),.join(), manualeach(proc.stdout)streaming under four different timeboxes, and inworker-lifecyclea long-lived bidirectional JSON-RPC peer driven throughproc.stdin. Three never spawn at all — they hand a base array toAPI.Env.commandand the test-agent provider does the launching.Node starts a fresh tsx process, not the running one. A CLI subprocess
should not inherit
--test, loaders or inspectors from the test runner'sprocess.execArgv, and naming the tsconfig on the command line means no suitehas to alter its environment.
Environment handling is untouched. It differs on purpose:
agent-cliinjects an isolated
HOMEso ACPX never reads the developer's config;props-cliallowlists precisely so an inheritedXMD_PROPS_*cannot corruptits precedence assertions; the rest inherit deliberately.
Resolution
/launchreaches each runtime the same way/bddand/expectdo after #188:deno.jsonfor Deno,tsconfig.node.jsonpaths for Node, and — for Bun — thereal workspace dependency plus the package's own
exports. No roottsconfig.json, no additional workspace package.What must stay true
packages/test-support, which rule 12 alreadyexempts as the host-adapter boundary for tests (♻️ refactor: own cross-runtime test infrastructure as @executablemd/test-support #188). This PR does not
restate that exemption.
packages/test-support/package.jsonkeeps the/bddand/expectexportsand the dependencies from ♻️ refactor: own cross-runtime test infrastructure as @executablemd/test-support #188, adding only
/launch.How to verify it
The four repository checks with the CI-pinned Deno 2.9.1: lint 0 errors,
typecheck clean, 145 passed / 0 failed, JSR
Success. Plus the ten suitesnamed explicitly under each runtime, so no unrelated suite changes the result:
Also green:
pnpm install --frozen-lockfile,bun install,pnpm exec tsc --project tsconfig.node.json --noEmit, the Bun entrypoint smokefrom #184, and
deno task gen:publish-workflowwith no generated drift.Scope
Included
packages/test-support/launch.tsand its three mappings.worker-lifecyclepassed its whole command line as one interpolated stringand now uses
command+arguments; XC4 claimed to test "the Denoentrypoint" and now says whichever runtime is running.
Intentionally unchanged
test:nodeandtest:bunstill enumerate a file list excluding all ten.A green CI job here proves nothing about them, which is why they are run
explicitly. Deriving the scope is Derive test:node and test:bun scope instead of naming test files #144.
bdd.tsandexpect.tsare untouched.cli-journalandtesting/clistill pass repo-relative fixture paths with nocwd, so they need the repo root as working directory. Both CI jobs runthere.
Risks and limitations
tsxbeing onPATHand on every suite'senvironment preserving
PATH. Both hold today — the Node job runs throughpnpm exec— but a suite that stopped passingPATHwould break launchingrather than fail an assertion.
if CI machines are slower than this one,
agent-cli(twelve launches) andsmoke(120 s) are where that would surface first.Scope confirmation