Test a Go-to-Market Change Before It Reaches Production
To test a go-to-market change before it reaches production on Cargo, ship the play switched off (isEnabled: false) and declare a pilot with defineBatch that runs it on 50 records. The pull request previews the pilot’s sends and credits, the merge runs it once, and a second pull request turns the play on after you have read the results. Before that, cargo-ai project check catches errors offline and cargo-ai orchestration action execute --dry-run shows an action’s sample output and price without calling the provider.
Software teams test code before it ships because a bug in production costs more than one caught on a branch. In go-to-market the cost is concrete: a scoring bug routes every inbound lead to the wrong rep, and an enrichment step on the wrong field spends credits on every contact. The steps below go from cheapest to closest to production.
1. Check the project compiles, offline #
cargo-ai project check
check validates the whole resource tree in one pass and reports every error it finds: an unknown reference, a bad slug, a cycle in the graph. It needs no state and makes no API calls, so it runs in an editor, in a pre-commit hook or in CI, and works before you sign in. On a connected repository, the Cargo check on the pull request fails on the same errors.
2. Dry run an action before you wire it in #
When a change adds a step, try the action on its own first:
cargo-ai orchestration action execute \
--action '{"kind":"connector","integrationSlug":"aiArk","actionSlug":"enrichCompany"}' \
--data '{"domain":"example.com"}' \
--dry-run
--dry-run checks your --data against the action’s inputs and prints a sample result in the shape of a finished run, with its credit price. No provider is called and no credits are used, so you can see which fields come back and what each record will cost before the step goes into a workflow. It covers connector and native actions. A tool or an agent runs your own workflow, so only a real run shows its output. Try Cargo without an account lists what else works signed out.
3. Read the plan on the pull request #
Open the pull request and read the comment Cargo posts. The resource table shows which plays, tools and agents change. The “On merge” table above it counts the records each play and execution runs on, and what it can send and spend. If the totals cross the approval limits, the check waits for the cargo:approve-impact label. Preview what a change will send explains the counts, and require approval before a high-impact change goes live covers the gate.
4. Ship the play switched off, then pilot it on 50 records #
This is the step that tests the change on real data. Take a new version of the play that enriches, scores and routes every new HubSpot contact. Deploy it switched off:
// infra/plays/inbound.ts
import { definePlay } from "@cargo-ai/cdk";
import { contacts } from "../models/contacts";
import { enrichScoreAndRoute } from "../workflows/enrichScoreAndRoute";
export const inbound = definePlay("inbound", {
model: contacts,
changeKinds: ["added"],
schedule: { type: "realtime" },
workflow: enrichScoreAndRoute,
isEnabled: false, // deployed, but runs on nothing yet
});
Cargo creates plays switched off, and isEnabled: false holds the switch to what the code says on every deploy that touches the play. In the same pull request, declare a pilot that runs the play on a bounded slice:
// outputs/2026-11-03-inbound-pilot/pilot.ts
import { defineBatch } from "@cargo-ai/cdk";
import { contacts } from "../../infra/models/contacts";
import { inbound } from "../../infra/plays/inbound";
export const pilot = defineBatch("2026-11-03-inbound-pilot", {
workflow: inbound,
data: {
kind: "filter",
model: contacts,
filter: {
conjonction: "and",
groups: [
/* contacts created this week, say */
],
},
limit: 50,
},
});
The plan lists the pilot under “Will execute once”, and the “On merge” table gives it its own row with its records, sends and credits. A play that stays off has no row, so the table describes exactly what the merge will do: 50 records through the new workflow. The merge runs the pilot once, and a ledger in the deploy state makes sure no later deploy runs it again.
Read the 50 runs in Cargo: the scores, the owners each contact was routed to, the fields written back to HubSpot. If something is wrong, fix the workflow and add a pilot with a new id, since an id that has run never runs again. When the results look right, open a second pull request that sets isEnabled: true.
For a change that sends, pick the pilot’s records with care. A pilot that emails 50 real contacts has emailed them, and a revert does not recall it. Use kind: "recordIds" with records you own, such as your team’s own test contacts, when the step sends.
5. Evaluate agent prompts on every pull request #
An agent’s system prompt is a string in a defineAgent file, so a pull request can change it and a pilot will not catch every regression. The project scaffold ships an evals/ folder with a promptfoo configuration: run it on every pull request that touches an agent. Evaluate an agent before production shows the setup.
6. Use a second workspace as staging, for changes you cannot pilot #
Some changes cannot be tried on 50 records: a new data model, a new connector, a rework of several plays at once. For those, deploy the same code to a second Cargo workspace first. Cargo supports staging and production as two workspaces, each with its own deploy state:
- Same code, different credentials. Write connector credentials with
workspaceEnv("HUBSPOT_TOKEN")and store a value under that key in each workspace, so the staging workspace talks to a test CRM account and production to the real one. - Select the workspace with
CARGO_WORKSPACE_UUID(or by logging in to it) before you plan and deploy. - Give staging its own state. A deploy refuses to run against a state that belongs to another workspace, so keep a separate checkout for staging and create its state there with
cargo-ai project state create --force. The checkout starts out pointing at production’s state, and--forcegives it its own; keep that pointer out of the commits you push.
A connected repository directory deploys to one state, so the production workspace keeps the deploy-on-merge connection and the staging workspace is deployed by hand or from your own CI with cargo-ai project plan and cargo-ai project deploy --yes. In staging, cargo-ai project deploy --draft stages plays, tools, agents, apps and workers as unpublished drafts and leaves their live configuration alone; running deploy again promotes them. The details are in Secrets and environments in the Cargo docs.
When a test misses something #
No test sees everything. When a bad change reaches production anyway, stop the play with isEnabled: false in a small pull request, then revert the change: roll back a go-to-market change covers both. The whole pipeline, from plan to rollback, is in CI/CD for go-to-market.
FAQ #
Deploy the play with isEnabled set to false, and in the same pull request declare a defineBatch on that play with a filter and a limit, such as 50 records. The merge runs the batch once. After reading the results, a second pull request sets isEnabled to true.
Yes. cargo-ai orchestration action execute with —dry-run prints a sample result in the shape of a finished run and the action’s credit price, with no provider called and no credits used. It works for connector and native actions, and without an account.
Yes, as two workspaces, each with its own deploy state. The same project code deploys to both, with credentials held per workspace through workspaceEnv() and the target selected with CARGO_WORKSPACE_UUID.