← All guides9 min read

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.

"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.

1. Why letting an agent push to Shopify is a foot-gun

A real Shopify load is irreversible enough to matter. Each loaded record:

  • Consumes Shopify Admin API quota on the destination store.
  • Creates a record (product, customer, order) that downstream systems — inventory, fulfilment, your accounting integration — will begin to treat as real.
  • Consumes platform credits on Graftport.

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.

2. The two-tier model

Every state-changing command in the Graftport CLI falls into one of two tiers.

Agent-allowed. No human gate. No --yes required.

  • All read-only commands — 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.

3. The exit-code-3 contract

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:

CodeMeaning
0Success.
1The command failed (an error occurred).
2Usage error (bad flag, missing argument).
3A 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.

4. The skill's first rule

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.

5. What the gate looks like in a session

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 type yes in 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.

6. Why this fits agencies running parallel migrations

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.

7. What the gate does not protect against

It is worth being honest about what the gate does and does not do.

The gate does protect against:

  • The agent kicking a real load without you knowing about it.
  • The agent retrying records or cancelling runs in a feedback loop.
  • A model that misreads its own output and decides everything is fine when it is not.

The gate does not protect against:

  • A bad mapping you approve. If the agent proposes a real run after a dry-run you both skim and miss a problem in, the run will go. That is what the dry-run safety net and the spot-check in the Data tab exist for.
  • Destination Shopify credentials being misconfigured. The CLI cannot tell whether your destination scope is a sandbox or a live store; you do.
  • Source data quality. The agent maps what is there. If the source store has 4,000 products with broken SKUs, the migration will surface that, but it will not invent the SKUs.

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.

8. The summary, in one paragraph

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.

Related reading

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.

Ready to migrate?

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.

Get started See the calculator
Related guides
Using the Graftport CLI with Claude Code: install to first publish
Set up the Graftport CLI and migration-engineer skill in Claude Code, then drive a real Shopify migration through the validate and publish l
Automating JSONata Shopify mapping with an AI coding agent
How the Graftport CLI lets an AI agent investigate source rows, fix JSONata mapping errors against structured validation codes, and publish
AI Shopify migration with a coding agent: the Graftport playbook
How to run an AI Shopify migration end to end with Claude Code, Cursor, Windsurf, or Copilot driving the Graftport CLI, with the costly step