Test a migrated app three ways
The scary part of a migration is not generating the new project — it is knowing whether the result actually behaves before you point anything real at it. HS-X makes the freshly-migrated dupe invocable through the production runtime router on your machine, from whichever client you think in: your terminal, your coding agent, or a bare HTTP request.
TL;DR — hs-x dev invoke <capability-id> runs one capability through the production router with fixture defaults; --object/--input/--install override them, --last replays the previous dispatch. Agents get the same engine as MCP tools (hsx.dev.capabilities, hsx.dev.invoke, hsx.dev.invoke_last). A running hs-x dev server exposes it to any HTTP client at POST /_hsx/invoke/<capability-id>.
One engine, three doors
hs-x migrate run leaves you with a net-new project — your legacy serverless functions as tools, webhook subscriptions as triggers, cards carried over with their React source. The question that matters next: does each one do what the legacy version did?
The answer mechanism is deliberately boring. There is exactly one invocation engine, and it is not a test double: your worker modules are loaded as-is, and the dispatch goes through the same runtime router a deployed Worker serves — the same payload validation, the same handler context, the same result envelope. The three doors into that engine only differ in who is knocking:
- The terminal, for you:
hs-x dev invoke. - MCP, for your coding agent:
hsx.dev.*tools. - HTTP, for scripts and CI:
POST /_hsx/invoke/<id>on the dev server.
Because the engine is the production path, a green invocation here means the handler, its types, and its envelope survive contact with the runtime — not that they passed a simulation of it.
The terminal
From the migrated project root (or anywhere, with --cwd):
hs-x dev invoke score-record \
--object '{"id":"d1","objectType":"deals","properties":{"amount":"50000"}}' \
--input '{"threshold":25000}'Fixture defaults fill anything you do not pass — a bare hs-x dev invoke score-record works seconds after migration. The output is the runtime's own envelope, pretty-printed; exit code 0 means the dispatch and the handler both succeeded, so it slots straight into a shell loop or a pre-deploy check.
When an invocation fails, fix the handler and re-run the exact same dispatch:
hs-x dev invoke --lastEvery invocation records its capability id and full payload to .hs-x/last-invocation.json; --last replays it, and explicit flags override individual parts. Typos get caught with the full menu: an unknown capability id error lists every id the project actually declares.
The agent
The HS-X MCP server exposes the same engine to coding agents. Three tools carry the loop:
hsx.dev.capabilities— list every worker and capability id in the project, with kinds. This is the discovery step, so the agent never guesses ids.hsx.dev.invoke— dispatch one capability;input,enrolledObject, andinstallare optional structured arguments over the same fixture defaults.hsx.dev.invoke_last— replay the recorded dispatch after an edit.
That triad is a complete debug cycle: discover, invoke, read the envelope, edit the handler, replay. Pointed errors flow through verbatim — an agent that asks for a capability that does not exist is told what does, in the same message.
Raw HTTP
Start the dev server and every capability gets an HTTP door:
hs-x dev --port 8787curl -s -X POST http://127.0.0.1:8787/_hsx/invoke/score-record \
-H 'content-type: application/json' \
-d '{"input":{"threshold":25000}}'The body takes the same optional input / enrolledObject / install keys; the response wraps the runtime envelope with the worker and kind that served it. This is the door for CI smoke jobs, REST clients, and anything else that speaks HTTP and nothing else.
What fixtures cover, and what they do not
Fixture invocation proves the handler logic, the capability wiring, and the runtime contract. Two things it deliberately does not fake:
- HubSpot API calls. A handler that uses
ctx.hubspotneeds a connected portal install to authenticate as — the same requirement production has. Run the fullhs-x devsession with a connected account for that leg. - Pre-cutover decisions. Migration findings like a legacy webhook receiver URL or an endpoint function's public contract are listed by
hs-x migrate runand stay visible in the generated handler stubs. Testing does not make those decisions for you; it tells you the code around them holds.
