Human approval gates for AI Shopify migrations: the Graftport contract
Why an AI coding agent should never push to Shopify on its own, and how Graftport's two-tier CLI contract enforces a human gate on every cost-incurring command.
Why an AI coding agent should never push to Shopify on its own, and how Graftport's two-tier CLI contract enforces a human gate on every cost-incurring command.
"AI does X autonomously" makes a great demo and a terrible production posture for anything that costs money or is hard to undo. A Shopify migration is both. This guide is the explanation — and the contract — for how Graftport keeps an AI coding agent on a short leash for the steps that matter, without slowing down the loop where it actually helps.
The conceptual overview lives at AI Shopify migration with a coding agent. This guide is the trust and safety angle in depth.
A real Shopify load is irreversible enough to matter. Each loaded record:
None of that is catastrophic in isolation. Graftport is idempotent, so a re-run does not duplicate. But agentic systems fail in correlated ways. A model that gets one mapping subtly wrong and then runs a real load on every resource in parallel can burn through a day's worth of Shopify rate limit and produce thousands of records that, while not duplicates, are uniformly slightly wrong. The fix is not technical — it is procedural. You want a human in the loop between "the agent thinks this looks good" and "Shopify writes to disk."
The temptation, especially in a polished agent UI, is to remove friction. We chose the opposite default: the friction goes on the expensive commands and only the expensive commands.
Every state-changing command in the Graftport CLI falls into one of two tiers.
Agent-allowed. No human gate. No --yes required.
migrations list, mappings show,
runs status, records failures, source rows, and the rest.mappings publish — a metadata write that flags downstream
records for re-load on the next run. The next run is itself
gated.runs start --dry-run — computes the full payload Shopify would
receive without pushing it. Zero load cost.Human-gated. Interactive yes required. A human operator can pass
--yes to script the CLI; the agent is forbidden from doing so.
runs start without --dry-run — the real load. Costs Shopify
API calls and platform credits per record loaded.runs cancel — destructive; may lose in-flight work for records
partway through.records retry — re-loads one record; small but real cost.The boundary is deliberately drawn between "things that can be undone with another publish" and "things that move money or destroy state." Everything cheap and reversible is on the agent's side. Everything that is not is on yours.
The gate is enforced both at the CLI and at the skill level. At the
CLI level it works like this: when you call a human-gated command,
the CLI prints the resolved action — and for runs start, a live
cost estimate — to stderr, then blocks on standard input waiting
for the literal string yes. Anything else, or a closed stdin, and
the command exits with code 3.
Exit code 3 is distinct from the rest:
| Code | Meaning |
|---|---|
0 | Success. |
1 | The command failed (an error occurred). |
2 | Usage error (bad flag, missing argument). |
3 | A human-gated action was declined, or validation failed. |
The distinction matters because the agent (and any wrapping script)
needs to tell "you said no" apart from "something broke." A
declined gate is not an error condition — it is the system working
as designed. The agent's correct response to a 3 is to stop,
report what happened, and wait for instructions.
The CLI gate alone is necessary but not sufficient. A --yes flag
exists on gated commands precisely so that a human operator scripting
the CLI in CI can bypass the prompt for an approved batch. An agent
with access to that flag could, in principle, pass it.
The bundled skill — the Markdown document
graftport skill install writes to your agent's instruction file —
makes this its first rule. The skill explicitly tells the agent:
never pass --yes on a human-gated command. The agent's role is to
compose the command, present it with its rationale and the cost
estimate, and stop. The human types yes.
This is a real constraint, not a guideline. The skill is the same
document for every supported agent — Claude Code, Claude Desktop,
Windsurf when installed by graftport skill install; Cursor,
Copilot, AGENTS.md when pasted in manually. Every agent that reads
the skill reads this rule.
For the install mechanics, see Using the Graftport CLI with Claude Code.
A typical Claude Code session, after a clean dry-run, ends with the agent proposing the real run:
graftport runs start mig_8f2a
When you (or the agent's harness) executes that, the CLI's stderr shows the resolved action and the projected cost. Stdin blocks. The agent's chat reports something like:
Validation passed at 50 rows and the dry-run completed cleanly. Proposing the real run on
mig_8f2a. The CLI is asking for your confirmation — please typeyesin the terminal if you want me to proceed.
You decide. Type yes and the run begins; type anything else and
the command exits 3 and the agent stops. There is no path through
the system in which the agent ships a real load without that
explicit step.
The same shape applies to runs cancel and records retry. The CLI
asks; you answer.
For an agency or a multi-brand parent running several Shopify replatforms in parallel, this model is more than a safety feature — it is the operating model.
A senior migration engineer can supervise four or five concurrent agent sessions because the supervision is only the gated steps. The agent does the investigative work, the JSONata iteration, the validation, and the dry-runs on its own across every project. The engineer's attention focuses on the points where money moves: the real loads, the cancels, the retries.
That is genuinely different from the old model, where a senior
engineer had to drive every mapping iteration by hand because there
was no agent capable of doing the boring middle. The cost of
parallelism was a body per project. With the gate model, the cost of
parallelism is the time it takes to read a cost estimate and type
yes.
The mechanical implication: a junior team member can run the agent
sessions and a senior reviewer can approve the gated steps. The
audit trail — what the agent did, what was approved, when — is
already there. Every mappings publish carries notes; every run
records its mapping versions and outcomes.
For the broader agency angle, see Managing Shopify migrations as an agency.
It is worth being honest about what the gate does and does not do.
The gate does protect against:
The gate does not protect against:
The gate is a procedural rail, not a correctness oracle. It buys you the time and the opportunity to apply human judgement on the steps that matter. The judgement is still yours.
Every cost-incurring command in the Graftport CLI blocks for an
interactive yes. The agent is forbidden by its skill from
bypassing that gate. Declined gates exit with a distinct code. The
boundary is drawn around "real load," "cancel," and "retry" —
everything else is on the agent's side because it is cheap and
reversible. This is the contract that makes it safe to hand the
iterative work of a Shopify migration to an AI coding agent.
--yes becomes a script's responsibility.Convinced the gate is the right shape? Set up a migration at app.graftport.com/signup or read /ai-migrations for the install commands and the agent compatibility matrix.
Connect a source store, dry-run a migration, see the exact Shopify result before a single record lands. The same platform your team will use on go-live night.