This is an authoring skill for people writing this repo. It is not a
customer skill and does not install with npx skills add. The canonical
copy lives in .agents/skills so Codex, Claude Code, and Cursor all load
the same file (the last two via symlink).
Read CONTRIBUTING.md first. That file is the contract the validators
enforce; this file is the procedure.
One folder at the repo root is one customer skill. Install path:
npx skills add getcargohq/gtm-skills/<name>.
1. Pick the kind
| Kind | Frontmatter | Job |
|---|---|---|
| one-off | metadata.source: one-off |
Runs once in a turn: exact cargo-ai command and a price |
| pipeline | metadata.source: cookbook |
Deploys a standing play. Customer copy says pipeline, never "cookbook" |
metadata.source must be exactly one of those two strings. The validator
uses cookbook as the internal discriminator. Do not put that word in
README, plugin listings, skill body, or examples. Customers learn: some
skills run once; some deploy a pipeline that keeps running.
If the same job exists both ways, they compete for prompts. Pin the seam in
evals/routing.jsonl: one case that must hit the pipeline, one that must
hit the one-off sibling.
Name the folder after the job, not the vendor. name in frontmatter equals
the folder name. The slug is what an agent matches before it opens the file,
so the kind has to be readable from the name:
| Kind | Shape | Examples |
|---|---|---|
| one-off | verb + object | enrich-company-data, score-leads, build-tam-list |
| pipeline | the standing practice | tam-building, account-scoring, crm-enrichment |
| migration | x-to-cargo |
clay-to-cargo |
A verb says "do this once." A standing noun says "keep this running." That
is how build-tam-list and tam-building stay apart, and why
score-leads pairs with account-scoring rather than score-accounts.
Do not give both kinds the same stem.
enrich-crm would read as a one-off next to enrich-company-data. A
pipeline that keeps CRM records filled is crm-enrichment. Contacts later
stay in that same slug. Accounts that are not in a CRM, if they land, are
account-enrichment. Planned pipelines stay nouns (contact-sourcing,
signal-based-tam, rep-cockpit). Do not rename existing skills to force
-ing vs -ment; the kind matters more than the suffix.
Inside a cookbook, resource slugs are snake_case: plays, workflows, tools,
agents, segments and folders (score_accounts, enrich_crm_contact,
account_enrichment, tam_tier_analyst, tam_tier_a,
crm_enrichment_plays), the same as the connectors and models the CDK
already requires it of. Workers and apps are the exception: the CDK requires
kebab-case for them. File names stay kebab-case (score-accounts.ts). A slug
is the resource's identity in cargo.state.json, so renaming one after a
deploy creates a new resource and orphans the old one: get it right before
the first deploy.
A model that extracts a system you hold (a CRM, a warehouse) pulls all the
data. Never filter or pick columns in its config: extract every record and
every column, and narrow in the SQL or the play filter that reads it. A
filter in the config is a second place the question is asked, invisible to
anyone reading the queries, and changing it means a redeploy and a
re-extraction instead of an edited query. A paid search source is the
exception, because there the search is the data source rather than a filter
on it: aiArk.fetchCompanies or a Sales Navigator search bills per returned
record, so its query in config is what decides what you buy.
Native models first. A worked example runs on Cargo native models, not a CRM connector, so it
deploys on a bare workspace. When the outcome needs accounts, contacts, opportunities or activity,
declare the shared gtm_* models exactly as references/native-models.md defines them;
scripts/check-native-models.mjs fails on drift. The CRM is the crm-backed variation.
2. Description (the only text before load)
Four parts, in order:
- The job in one sentence
Triggers:literal quoted phrases a user would type- Proper nouns (connectors, CRM, CDK)
Skip when:the sibling skill that should win instead
Quote any YAML value that contains colon-space. Unquoted, the installer skips the file with a warning nobody reads.
3. Write the kind
One-off
Copy the structure of enrich-company-data/SKILL.md:
- Setup (CLI install, login, session stamp)
- Do the job (exact
cargo-ai orchestration action execute/execute-batch) - Example (see below)
- What it costs (table of
integration.action→ credits) - Worth knowing / Going further / star ask
Slugs and prices are not yours to invent. validate.ts asserts every
integrationSlug/actionSlug and every credit number against
getcargohq/cargo-skills playbooks. Copy from a sibling, then run
node scripts/validate.ts.
Register: skills.sh.json, hooks/skill-loads.sh, root README.md table.
Pipeline
Copy the section order from tam-building/SKILL.md. The validator requires
these headings:
- Honest banner:
**State: to-be-approved.** ## The outcome## Example(see below)## Put it in your project(copy the procedure fromtam-building; each skill carries its own)## What you will be asked— derive before ask; more than ~4 asked rows means you skipped lookups## What you can change— offer unprompted; every variation has a cost## What should not change— name the concrete symptom if violated## Done when## What it costs## Composes into
Folder:
<name>/
SKILL.md
README.md
infra/ # or models/, plays/, agents/ — whatever the outcome needs
references/ # supporting agent docs; never a nested SKILL.md
evals/acceptance.md
Isolation: every model and connector the resources import lives in this
folder. No relative import may leave it. Two pipeline skills may both
carry an accounts model; the placing agent rewires to the project's
existing one.
Resources must earn their deploy:
- Do not wrap a single connector action in a tool. That is a one-off.
- Do not declare a segment that restates the play's own filter.
- Do not add
examples/example.mduntil a live install has sanitized audit findings, mappings, a plan summary, and lookup-backed credits. - A folder of CDK with no
SKILL.mddoes not belong at the root. Older examples live in history:git checkout 305cd88 -- <name>, then write the skill.
Credits. Pipeline markdown must not contain a numeric credit amount.
Fetch live prices at run time (cargo-ai connection integration get <slug>).
One-offs quote playbook numbers; pipelines do not.
Write the system you write back to. If the outcome updates a CRM
record, the play runs on that CRM extract and matches the CRM record id.
Do not sit a native unifyAccounts / ids map between the play and the
write — the run looks successful and nothing lands.
One shape in the file. A new pipeline's checked example runs on the
shared gtm_* native models (references/native-models.md); its
crm-backed variation names the HubSpot swap, and Salesforce and Attio adapt
that (connector, extractor, record-id, write action, fill-blank guard). A
pipeline whose whole job is a CRM (crm-enrichment, crm-deduplication) keeps
HubSpot as its checked example. No parallel branches.
CDK workflow bodies must compile. These fail cargo-cdk check / plan:
undefined/void, comma / SequenceExpression, Date#getTime().
Register: skills.sh.json (its stage, at its place in the order: the order is
what an agent suggests next), hooks/skill-loads.sh,
root README pipeline table, .github/data/approvals.json with
state: to-be-approved and empty evidence.
Both kinds: personas and the example
validate.ts requires these of every skill; the site renders them before
anyone installs.
-
metadata.personas, right aftermetadata.source: 1–3 ofsales-development,account-executive,revops,sales-leadership,marketing,gtm-engineering, primary first. -
## Example, in exactly this shape:## Example > Find work emails for the 40 people in webinar-attendees.csv. Illustrative output, fictional records: <table or fenced block, about 20 lines at most> <one closing line with the result in numbers>Fictional companies on the
.exampleTLD. A one-off's credits must add up from its own cost table; a pipeline quotes none and shows what the deployed play writes. Never a command in it, and never a table row that opens on a backtickedintegration.actioncell — the validator reads that as a price.
4. Finish
node scripts/build-catalog.mjs
node scripts/generate-llms-txt.ts
npm run typecheck
npm run validate
npm run format:check
catalog.json and llms.txt are generated. Edit SKILL.md, then regenerate.
Do not claim an outcome for a pipeline skill until it is approved (demo
workspace + two implementations in approvals.json). The banner in
SKILL.md must match that file exactly.
Anti-patterns
- A new root skill that is the same job as an existing slug with extra
words (
crm-account-enrichmentvscrm-enrichment) - A pipeline named like a one-off (
enrich-crm,score-accounts,build-tam) or a one-off named like a pipeline (tam-buildingfor a list today) - Customer-facing "cookbook"
- Hardcoded credits in a pipeline
- Stub walkthroughs that restate
SKILL.md PLACEHOLDERvalues that survive into a "done" template when the checked example can use real HubSpot (or whatever) property names- Asking the operator for anything a connector or live schema can answer