Launch
Run a finished pipeline on people your ledger has never seen, with plan and the dry-run as the go/no-go, then check the ledger knows every one
What you'll learn
Someone handed you a finished pipeline or a bundle folder, and you're about to run it on fresh data for the first time.
- what to read in plan and the dry-run receipt before you launch
- what the launch run leaves in gtme runs
- how to check that the ledger knows every person the run sourced, delivered or not
Goal: a done run of a pipeline you didn't write, and a ledger that now knows every person it sourced. SPEC.md, the file that fixes what gtme does, states the promise this guide checks in its Launch story:
starting a new pipeline against fresh data produces a receipted run and a ledger that now knows those identities.
Before you start
You need gtme and a pipeline with its input. Install covers the first. This guide launches hello.yaml, the three-person file from See it run, because it runs with no keys. A real pipeline has more steps and bigger numbers, and you read them the same way.
This guide needs no keys and sends nothing past your own disk. Its receipts show $0.03 spent, and that money is pretend: demo/enrich prices itself at $0.01 per record and calls no vendor. The one deliver step writes to out.csv in your folder.
If you were handed a bundle folder, run the steps from inside it with . in place of the file name. gtme run checks the bundle's hashes before it reads a record. Every shipped bundle calls a model or a vendor, so its armed run needs a key, and Your stack shows how to store one.
Steps
-
Make a folder with its own ledger, and fetch the pipeline and its input:
mkdir -p launch && cd launch export GTME_LEDGER=./ledger.db curl -fsSLO https://raw.githubusercontent.com/gtme-run/gtme/main/examples/hello.yaml curl -fsSLO https://raw.githubusercontent.com/gtme-run/gtme/main/examples/contacts.csvOn a real launch, leave
GTME_LEDGERunset, and rununset GTME_LEDGERif you exported it earlier, because your main ledger is the one that should learn these people. Here it keeps the practice launch out of your main ledger.Here's the file you were handed, without its comments. In a file you didn't write, read the source, the filter's threshold, and the deliver target first:
name: hello version: 1 source: use: csv/source with: path: contacts.csv columns: { full_name: Full Name, email: Email, title: Title, company_domain: Company Website } steps: - id: score use: demo/enrich with: cost_per_record_usd: 0.01 - id: keep use: sql/filter with: query: > SELECT identity_id FROM current_values WHERE field = 'demo.score' AND CAST(value AS INTEGER) >= 70 - id: out use: csv/deliver with: path: out.csv variables: score: demo.score note: demo.note idempotency: emailPipeline hello: source csv/source, then score (demo/enrich), then keep (sql/filter), then delivers with out (csv/deliver), once per email. Every step writes to the ledger, and cached, AI, and deliver steps read it first. -
Check that the data is fresh. Ask the ledger for one person from the CSV:
gtme show jane.doe@acme.comIt prints:
gtme: no identity known by key "jane.doe@acme.com"This checks one person, so treat it as a spot check; with a search source, check one person from the dry-run's list instead. If
gtme showprints a record, an earlier run already sourced this person. That's not a stop: the launch pays only for what's missing or past its cache window, andidempotency: emailmeans no one already delivered to this target gets it twice. -
Plan it. Plan, the first rung of the gate ladder, reads the file with no network and spends nothing. With a bundle,
gtme plandoesn't accept the folder yet, so rungtme run . --simulate, an offline rehearsal that spends and keeps nothing, and read its problems and resolved variables instead:gtme plan hello.yamlIt prints:
pipeline hello (version 1) ... 2. score [enrich] — demo/enrich@1 ... cache: 30d est/record: $0.0100 ... send surface: 1 deliver step(s) out → csv/deliver (touch scope: hello) ... plan ok — nothing has been spentRead three lines.
plan okmeans every field and credential the file needs resolves. Multiply eachest/recordby your row count to get the most that step spends: here $0.0100 times 3 rows, so $0.03. A?means the step is unpriced or metered, and the dry-run receipt shows its cost.send surfacelists every step that sends when you launch. Here that'sout, and its target is a file. A step on that list you didn't expect is a no-go, so ask whoever handed you the file. -
Dry-run it. This spends the pretend $0.03 and sends nothing. On a pipeline with vendors, this is the rung that spends real money:
gtme run hello.yaml --dry-runThe output is similar to the following:
dry run: deliver steps will resolve and receipt their variables, but nothing sends ... run 01M3K26QK8Y3VJZP3CX0T6G5XC — done (dry run — nothing sent) step adapter in out empty cached filtered failed cost avoided source csv/source 0 3 - 0 - - $0 - score demo/enrich 3 3 - 0 - - $0.0300 - keep sql/filter 3 1 - 0 2 - $0 - out csv/deliver 1 0 - 0 - - $0 - out: resolved variables for 1 record(s) — review, then run again without --dry-run to arm: jane.doe@acme.com note: "synthetic — demo/enrich called no vendor" score: "100" total: $0.0300 (estimated) spentThis receipt is the go/no-go. The
sourcerow'soutshould match the rows you were handed, and the total should sit inside plan's bound.filtered 2onkeepshould be a number you can explain: Bob and Carol scored under 70.Then read each resolved variable, because that's exactly what the launch writes. The decision record, ADR-019, makes these variables the thing you approve. On a live target such as an Instantly campaign, the dry-run also prints a
preflightline. It checks that the campaign is active and uses every variable, and a blocked check is a no-go. -
Launch it. This is the armed run, the command with no flag. It spends only on what the ledger doesn't already have, and it writes Jane to
out.csv:gtme run hello.yamlThe output is similar to the following:
run 01M3K26QM67AH93C1PDRTQ6R4P (hello) ... out [info]: csv/deliver: wrote 1 row(s) to out.csv ... run 01M3K26QM67AH93C1PDRTQ6R4P — done step adapter in out empty cached filtered failed cost avoided source csv/source 0 3 - 0 - - $0 - score demo/enrich 3 0 - 3 - - $0 $0.0300 keep sql/filter 3 1 - 0 2 - $0 - out csv/deliver 1 1 - 0 - - $0 - total: $0 spent, $0.0300 avoided via cache (3 records skipped)That's the receipt the invariant asks for: status
done, 3 records sourced, and a total with its cost.scoreshowscached 3because the dry-run already paid for those scores.out 1is the delivery the dry-run held.The source read the CSV again. A live source, such as a CRM search, can return people between the dry-run and the launch that you never read, and a group pins the list you dry-ran. If the receipt says
failedinstead, it names the step and the reason. Fix that, then finish the same run withgtme run hello.yaml --resume RUN_ID, using the id on the receipt's first line. -
Check that the ledger knows every person the launch sourced. This query lists each identity in the newest run, the last step it finished, and how many fields the ledger holds for it:
gtme query "SELECT i.identity_key, r.state, count(DISTINCT f.field) AS fields FROM run_records r JOIN identities i ON i.id = r.identity_id LEFT JOIN field_values f ON f.identity_id = i.id WHERE r.run_id = (SELECT max(id) FROM runs) GROUP BY i.id"It prints:
{"fields":6,"identity_key":"jane.doe@acme.com","state":"out"} {"fields":6,"identity_key":"bob@globex.io","state":"score"} {"fields":6,"identity_key":"carol@initech.dev","state":"score"} 3 rowsThere's one row per CSV row, and each person has six fields: four from the CSV and two from
score. A0infields, or fewer rows than thesourcerow'sout, would mean the ledger missed someone. Bob and Carol stopped atscore, sincekeepfiltered them, and the ledger kept their scores anyway, sogtme show bob@globex.ioprints all six of his fields.
What you have now
A launched pipeline: one done run with a receipt, and a ledger that knows all three people from the CSV. out.csv holds the one person the pipeline delivered:
cat out.csv
identity_key,note,score
jane.doe@acme.com,synthetic — demo/enrich called no vendor,100
To have Claude Code launch the next file this way, paste this line. The agent climbs to the dry-run and leaves an armed run on a live target to you (For agents):
Launch PIPELINE: check with gtme show that a sourced person is new, plan it, dry-run it, then stop and show me the dry-run receipt. After I arm it, confirm with gtme runs and gtme query that the ledger knows every sourced person.
Replace PIPELINE with the file or bundle folder you were handed.
Next
gtme runlists every flag of the launch command, including--resume.- Facts have provenance explains which source and run wrote each field
gtme showprints. gtme querylists every flag of the command the ledger check used.