Systems Lab

Agent skill

gtm-workflow

Triggers when a user asks to build, create, update, run, test, schedule, deploy, host, upgrade, inspect, open the inspection UI, or delete a saved GTM workflow, its result table, runs, or diagram in a GTM workspace, with phrasings like "build a workflow that scores our inbound companies", "run Score inbound accounts", "put it on a weekly schedule, hosted", or "upgrade the workflow runtime".

activeNeeds a keyActs undeclared4,043 words

Filed under Prospecting and list building.

From eliasstravik/gtm-skills · 7 skill entries · 13 · pushed 2026-09-21

What it does when it runs

Triggers when a user asks to build, create, update, run, test, schedule, deploy, host, upgrade, inspect, open the inspection UI, or delete a saved GTM workflow, its result table, runs, or diagram in a GTM workspace, with phrasings like "build a workflow that scores our inbound companies", "run Score inbound accounts", "put it on a weekly schedule, hosted", or "upgrade the workflow runtime". Owns the workflows folder, which holds workflow code, tables, runs, schedules, diagrams, and Vercel deploys. Not for the workspace, ICPs, or personas themselves (gtm-workspace, gtm-icp, gtm-persona), or one-off fit checks that are not saved (gtm-qualify-prospects).

Automated analysis of the skill and the 237 files bundled beside it. A skill’s own description is written to be selected by an agent, so it describes the job and not the dependencies.

Keys and connectors you must supply
  • AI_GATEWAY_API_KEY
  • ANTHROPIC_API_KEY
  • APOLLO_API_KEY
  • AUTH_TOKEN
  • BLITZ_API_KEY
  • CAL_WEBHOOK_SECRET
  • CRON_SECRET
  • CUSTOM_API_KEY
  • DATABASE_API_KEY
  • DATABASE_AUTH_TOKEN
  • GTM_ADMIN_API_KEY
  • GTM_CONNECTIONS_READ_SECRET
  • GTM_CONNECTIONS_VERCEL_TOKEN
  • GTM_NOTIFY_SECRET
  • GTM_RUN_SECRET
  • GTM_SECRET_API_KEY
  • GTM_TEST_API_KEY
  • GTM_VIEWER_LINK_KEY
  • HUBSPOT_API_KEY
  • HUBSPOT_PROD_KEY
  • MIGRATE_LOCK_KEY
  • MONID_API_KEY
  • NEVER_RETURN_THIS_KEY
  • NEXT_PUBLIC_BAD_API_KEY
  • PUBLIC_TEST_API_KEY
  • RUNTIME_READ_SECRET
  • SERVICE_API_KEY
  • VERCEL_API_KEY
  • VERCEL_OIDC_TOKEN
  • VERCEL_TOKEN
  • WRITE_LOCK_KEY
Hosts it reaches
  • api.contactout.com
  • api.hubapi.com
  • api.monid.ai
  • api.vercel.com
  • apify.com
  • buymeacoffee.com
  • connections.example
  • console.neon.tech
  • developers.clay.com
  • docs.brightdata.com
  • docs.peopledatalabs.com
  • dotenvx.com
  • evil.example
  • evil.test
  • feross.org
  • gtm-acme.vercel.app
  • linkedin.com
  • local.drizzle.studio
  • mcp.example
  • mcp.monid.ai
  • monid.ai
  • opencollective.com
  • other.test
  • outside.example.com
  • paulmillr.com
  • paypal.me
  • private.example
  • private.example.com
  • registry.npmjs.org
  • share.example
  • tsx.hirok.io
  • vercel.com
  • workflows.example.com
  • www.clay.com
  • www.example.com
  • www.linkedin.com
  • www.patreon.com
  • xn--r8jz45g.xn
Tool permissions it declares
No allowed-tools in the frontmatter. It does act, so it runs under whatever permissions your session already grants.
Actions present in the files
shellwrites filesnetwork

Ask about gtm-workflow

Opens your assistant with this page's verified links already in the prompt.

Is this safe to install?ClaudeChatGPT
Adapt it to my stackClaudeChatGPT
What else do I need for it to workClaudeChatGPT
Rather ask a human? Talk to Cheetah
git clone --depth 1 --filter=blob:none --sparse https://github.com/eliasstravik/gtm-skills.git /tmp/gtm-skills
git -C /tmp/gtm-skills sparse-checkout set "skills/gtm-workflow"
mkdir -p ~/.claude/skills/gtm-workflow
cp -R "/tmp/gtm-skills/skills/gtm-workflow/." ~/.claude/skills/gtm-workflow/

Picked up without a restart. A project skill of the same name is shadowed by your personal one. For one repository only, swap ~/.claude/skills for .claude/skills. Claude Code docs ↗

The folder is the same in every client that implements the format — 46 of them — so if yours is not above, only the destination changes.

Before you install: this skill will not complete its job on a bare agent. It needs AI_GATEWAY_API_KEY, ANTHROPIC_API_KEY, APOLLO_API_KEY, AUTH_TOKEN, BLITZ_API_KEY, CAL_WEBHOOK_SECRET, CRON_SECRET, CUSTOM_API_KEY, which you have to obtain separately.

Reproduced in full from eliasstravik/gtm-skills/blob/2f986ae36d384bded1df28ba51fee25a672fdf4d/skills/gtm-workflow/SKILL.md, which is licensed MIT (repository). 4,043 words, 11 headings.

GTM Workflow

Trigger

Apply this skill when a request concerns a saved workflow: creating, changing, running, scheduling, deploying, upgrading, inspecting, opening its UI, or deleting one, or its table, runs, or diagram.

When creating or changing a workflow that enriches connections or followers and their current companies, load gtm-workflow-enrich-network when installed for that recipe; this skill still owns its lifecycle. Ordinary runs of an unchanged workflow need only this skill.

Show the intent menu only when the user's entire request is /gtm-workflow or /gtm-workflows, with no action or other words. Ask Choose a workflow action. with three choices: Open GTM Workflows, Create a workflow, Manage a workflow, and wait. Manage asks which workflow and action: change, run/test, schedule/deploy, upgrade/repair, or delete. Bare entry performs no creation, execution or deployment.

Open GTM Workflows is already an explicit Open request. Resolve the verified viewer URL and return it immediately using entry links; do not show the intent menu or ask the user to choose Open again. Loading this skill does not turn an explicit request into a bare invocation. Other explicit actions also go directly to their procedure. If Open finds no workspace, explain workspace setup; if the workspace has no workflows, show its empty list.

Scope

This skill owns <workspace>/workflows/, a Vercel Workflow runtime on Nitro copied from templates/ on first use: workflow files, db/tables/, migrations, vercel.json, .env, the local database, runs, diagram pages, and the deployed copy. Runs execute locally against this workspace's own Postgres in data/pg by default; they target the deployed copy when the user asks or the host states there are no local runs, and hosted results are read through the deployed copy's query route. Local Connections uses a trusted component outside workflows/; deployed Connections runs inside the existing private Workflows project. For setup, key discovery, connection changes and component upgrades, read Connections. The remaining workspace files outside workflows/ belong to the other gtm skills.

Inputs

The request; the workspace found by the contract; the ICP or persona whose criteria the workflow copies; references/local.md for commands, keys, and verified facts; references/deploy.md for hosting.

Roles

The user's request authorizes routine code changes and real runs within the configured row and cost caps. State the estimate and proceed; confirmation is reserved for destructive actions under the shared interaction rules. The user connects the workflow project through shared standalone setup or optional gtm-agent setup and chooses the AI backend once per workspace in .env; the agent writes, runs, and reports. Deploy is the push.

Procedure

When persisting enriched people or companies, follow shared profiles for storage, identity, freshness, paid attempts and scoped Data views. Its two phase workers and common budget replace the generic self-fan-out pattern below for multi-phase enrichment. The profile tables are runtime tables in schema gtm; scripts/migrate.mjs migrates them with everything else, and upgrades preserve them.

Talk by the six rules in interaction; reproduce the dialogues. Workflow slugs follow the contract's slug rule; the display name is the doc comment's first line.

JobDo
CreateFirst use on a personal computer: run shared local setup from Connections, which creates an empty registry and prepares inspection without execution. Hosted authoring uses the already configured runtime. Start npm run viewer for inspection and npm run dev only for deliberate execution. Ask where the rows come from. When the workflow reaches people (an approval on a stage, or a notify call), ask which Slack channel it should post in, one question: a channel id (C0…, shown at the bottom of the channel's About tab) or "here", which means the channel of this conversation when the agent knows its id; the answer becomes a NOTIFY constant at the top of the workflow file, passed as notify to every stage and notify call, never a project variable. There is no per-workflow backend question: agent stages read GTM_AGENT_BACKEND from .env at run time (claude or codex for the author's subscription on a personal computer, unset for AI Gateway), so one file runs on the subscription locally and on the Gateway hosted. At scaffold on a personal computer, when claude or codex is on PATH and no Gateway key is present, set it once and say so; otherwise leave it unset. Author literal viewer.businessGraph using viewer, with labels and branches checked against the new code. Write workflows/<slug>.ts from example-scores.ts, or from example-research.ts when it has an agent stage, its table in db/tables/<name>.ts, register both in workflows/index.ts and db/tables/index.ts (keep the example workflows and their tables; removing a table in the same generate as adding one makes the generator ask a rename question that no host can answer), run npm run db:generate -- --name <name>, run npm run viewer:register once to assign the new workflow identity, rebuild, open its Diagram page, commit, close with what was created and the Open GTM Workflows entry. On a host without local runs: no localhost link, ever; after the push, read GET /api/link/<slug> on the deployed copy every 10 seconds, up to 3 minutes, until git merge-base --is-ancestor HEAD <commit> holds for the commit it returns, and only then post its verified viewerUrl; a reply written before that holds carries no links and says the deploy is under way. When the user asks to run, execute the requested scope after checking keys and caps, stating the estimate without another confirmation; when no connected project exists on a host without local runs, close instead by naming the connection steps in deploy.md.
UpdateChange the workflow and its viewer.businessGraph together, including any new business decision branches and owner details; preserve viewer.id, validate the metadata with node scripts/build-viewer.mjs, refresh its criteria copy, add columns (then db:generate and restart); preserve existing data; close with what changed and the Open GTM Workflows entry.
RunTarget: local by default on a personal computer; the deployed copy when the user asks or the host states there are no local runs, which needs a connected project and, first, Deploy's readiness check. Pre-run check: the row count, rows × estimate against both caps, every connection the workflow uses against the target inventory in Connections, and whether the workflow is new or changed since its last run. A requested run, whether one or many rows, proceeds without a permission or test-first question: state the row count and estimated cost, then run the requested scope. A missing key or exceeded cap stops execution and names the blocker. Start through the route, poll the read route, close with the result in plain words: done, failed, skipped, cost; no links, unless they changed since the thread last showed them. When the read route lists a pending approval, stop polling and ask the person in one message (the tool, its input in plain words, the price when known), then decide it through the approve route with their answer and its reason, and resume polling; a run with stream on may be narrated from the stream route while it runs. When a local run follows a hosted one, say it may re-spend on rows the hosted copy already did.
Open UIFor “Open GTM Workflows UI” or an explicit link request, follow entry links. Resolve the discussed workflow by stable identity; without one, open the list. Check readiness. Locally start only npm run viewer if needed. Opening the UI never executes a workflow, runs a migration, or deploys a missing host. Report a concrete setup blocker when unavailable. Always return the entry when explicitly asked, even if already shown.
ShareHosted only: read grants using /api/viewer/service?v=2&workflow=<id>&op=grants. Show the selected tabs and the current permitted-data summary before an explicit save. POST saveLink with {views, policy, save}. policy is the displayed hash; save: true explicitly approves scope or policy changes. Copy without changes reuses the link. Default is Diagram with no expiry. POST revokeGrant with the active ID to turn it off. Re-enable creates a fresh secret. Use the exact returned URL. See viewer.
DeployDeploy is the push; follow deploy.md for the one-time connection and the readiness check. A push never touches a run already in flight: it finishes on the code it started with. The hosted copy runs every agent stage through the Gateway whatever .env says locally.
UpgradeCompare workflows/ to the installed templates/; say what differs; a workspace from before the Postgres runtime (it has db/tables/cache.ts) is not converted by this skill: say so and stop; update template-owned lib/, server/, viewer/, connections-ui/, viewer-server/, share-server/, scripts/, drizzle-runtime/, nitro.config.ts, drizzle.config.ts, drizzle-runtime.config.ts, tsconfig.json; copy skills/ from the template when the workspace has none; follow runtime reliability to merge dependencies and startup scripts into package.json, preserving user-added dependencies and custom commands, then copy the template's package-lock.json over the workspace's, delete node_modules, run npm install and confirm with npm ci --dry-run (installing over the old lockfile drops other platforms' packages and breaks the hosted build, and a lockfile generated from nothing is rejected by npm ci); regenerate workflows/index.ts, db/tables/index.ts, and skills/index.ts, preserving each workflow entry's viewer, data, intake, and other authored properties, including imported metadata files; never touch other files in workflows/, db/tables/, skills/, drizzle/, vercel.json, .env, data/; merge generated-asset and .env.* ignore entries; install the trusted component with shared setup --upgrade as Connections specifies; run npm run viewer:register only for entries missing an identity; build, start npm run dev once so the local database is migrated, then verify npm run viewer and the hosted private/share builds. Read each existing workflow and add or correct its businessGraph before the build. Preserve all workflow IDs. Restart the local server through npm run dev and verify its startup settings and a diagram page; hosted upgrades verify the deployed copy after the readiness check.
DeleteRemove the workflow file, its registry entry, and its vercel.json cron; keep the table and its data and say so; when a project is connected, say the hosted copy drops it and its schedule as the save deploys.

For Create, Update and Upgrade, read viewer. Keep a valid business diagram and, for every Data tab, a valid viewer.sharePolicy. Build before saving; missing or mismatched sharing metadata is an authoring error. Preparing a policy makes Data selectable; changing a public link's access still requires an explicit sharing request.

Every save: one sentence on what will change, pull first when origin/main exists, edit through the host's write path, commit on main with a plain-language message, push when a remote exists, verify the commit (and that it reached origin/main when a remote exists), close with what was created, changed, or deleted.

On a host that states there are no local runs: the scaffold deletes env.example instead of renaming it, because the values live in the host's environment; every run targets the deployed copy after the push and after Deploy's readiness check; Upgrade verifies through the deployed diagram page after that check instead of restarting a server. Two rules hold on such a host whatever else the agent does:

  • Links: a dev server or a local build may be started as a check, but nothing it serves is ever posted. The only links a reply may carry are the verified viewerUrl the deployed copy's link route returned after the readiness wait; localhost never appears in a reply.
  • Keys: read GET /api/connections through the protected workflow transport for presence and declared usage, including unused services. Use the saved Note as the service name and the exact variable for code, following Connections naming. Direct missing-key entry to the verified connectionsUrl from the private link route. Never collect keys in chat. Saved changes become active after a deliberate deployment; presence does not prove validity.

Conventions the code follows

  • Linked result tables: linked data defines the optional data registry property and the read-only People/Companies-style viewer. Register it when users need to browse related records. Its private Data link comes from the same link route. Preserve viewer, data, and intake during upgrades. Sharing is an explicit private-viewer action; it requires a versioned table/column/relationship/row policy described in viewer.

  • Step choice: every step is the plainest kind that does the job. Deterministic work (parsing, arithmetic, a fetch, a provider or gateway API call with known inputs, a Slack post) is a plain "use step" function with no model. Judgment over text the workflow already holds (scoring, classifying, extracting, drafting) is a single-shot AI step, generateObject with a schema. An agent stage (runAgent) only when the user names an agent, or the work needs the model to choose its own tool calls as it goes (find the right endpoint, research across pages) and no sequence of plain and AI steps can be written in advance; a stage that would always call the same tools in the same order is those steps instead. When the user asks for an agent where steps would do, say so in one sentence and build what they asked.

  • Rows come from defaultInput (inline), a provider or API call inside a "use step" function, a table another workflow fills, or an inbound webhook (export const intake = defineIntake({ secretEnv, signature, eventId, toRow }) from lib/intake.ts, registered as intake in workflows/index.ts; POST /api/intake/<slug> verifies the sender's signature over the raw body, drops redeliveries by event id for 30 days, and starts one run with the mapped row); never from a workspace file or local-only data. Input is RowsInput from lib/rows.ts, { rows?, maxRows?, maxSpendUsd? }, each row an object with a required string key, the table's primary key; the workflow's constants are the defaults; the start route merges a POST body over defaultInput, so the limited run is POST { maxRows: 1 }.

  • runRows({ rows, table, step, maxRows, maxSpendUsd, estimateUsd, freshForMs, concurrency?, fanOut? }) from lib/rows.ts is the loop; step(row) is a plain async function in workflow scope that awaits "use step" functions in sequence and returns { ...columns, costUsd }. Tables are passed by name and resolved through db/tables/index.ts. Every workflow passes fanOut: { workflow: <its own function>, input, chunkSize }: above that many rows the run only splits the list into child runs of itself, four at a time, each with its exact share of the caps, and adds up their totals, so a list of any size is one run with one result. chunkSize is 100 for a workflow of plain steps and 20 for one with an agent stage: the engine caps a run at 25,000 events and an agent row costs about 100 of them. Cancelling the parent cancels its children.

  • Every result table has key, updated_at, cost_usd, error. All database I/O, every cached() and generateObject call happen inside "use step" functions declared as async function name() { "use step"; }, never arrow or method form; every paid or AI step sets name.maxRetries = 0.

  • Agent stages: an agent inside a workflow is always runAgent() from lib/agent.ts, called from a plain async function in workflow scope (the stage function, an agent node), never WorkflowAgent directly and never inside a "use step" function; the helper makes every model call and tool call a step, races the time limit against sleep(), cleans the schemas, and returns real cost. Its config is the whole authoring surface: model and reasoning (defaults GTM_MODEL and GTM_REASONING on the project), instructions, skills (names from skills/index.ts, each a text module under skills/), tools (mcp servers by name with url, keyEnv, allow, maxCalls; web.fetch free, a step; web.search executed by AI Gateway (true or "gateway", Exa, any model, no key) or by OpenAI ("openai", openai/* models only), inside the model call rather than as a step, so it cannot be approved or call-limited; custom tools whose execute is a "use step" function), approve (tool names requiring destructive-action confirmation or an explicitly requested workflow review; never add routine approval gates by default; the row waits on a hook and the run's read route lists the request until POST /api/runs/<id>/approve decides it; set timeout in days when approvals may wait), stream (every model and tool event on GET /api/runs/<id>/stream), maxSteps, maxUsd (soft: the agent stops using tools after the call that crosses it and writes up what it has), timeout (a duration string; the row fails), schema (or none for plain text), prompt or messages, and, for anything else WorkflowAgent takes, agent (constructor options: sampling, provider options, prepareStep, prepareCall, callbacks, telemetry, contexts) and call (per-call options: toolChoice, activeTools, transforms). Schemas use nullable fields, never optional ones, and no string formats (z.string(), not .url() or .email()); the description carries the intent. Never pass timeout: or AbortSignal.timeout() to the SDK; never build MCP clients or tools by hand. example-research.ts is the reference.

  • A single-shot AI step is generateObject with GTM_MODEL inside a step; anything that uses tools is a runAgent stage. The stage's backend is decided per run from the frozen environment: GTM_AGENT_BACKEND (claude or codex: the author's subscription, one step, this machine only; MCP servers and web tools carry over; codex needs each MCP server's allow list) or the Gateway when unset. A stage that uses custom tools, approve, stream, or messages, and every stage on the hosted copy, runs on the Gateway regardless. backend on a stage overrides the variable for that stage only. No other AI path exists.

  • Criteria: export const criteria = \…`holds the ICP or persona text, first line naming it; backticks and${` are escaped. Nothing else from the workspace is deployed; the copy stays until the next Update.

  • Doc comment: first line is the title, second paragraph the one-sentence summary; the diagram page reads both.

  • Viewer metadata: author literal viewer.businessGraph with stable node IDs, human labels, input/action/decision/output kinds, explanations and meaningful decision edges. Keep provider/caching details in explicit owner fields. Review the diagram against the actual workflow code on Create, Update and Upgrade; preserve viewer.id. See viewer for the schema and validation.

  • Where to look: follow entry links after create/change, deploy/upgrade, ordinary runs, and explicit requests. Return the canonical private viewerUrl as Open GTM Workflows. This is separate from issuing an external share grant.

  • Connections: read the target inventory before writing a paid or AI step and in every pre-run check. Name a missing connection once and open the trusted Connections form; use its workflow-declared gateway relationship when deciding which key is required. Author literal viewer.connections alongside the business graph. Follow Connections for unknown keys, activation state and safe credential entry.

  • Gateways: a paid step that uses a provider found through a gateway tool the host exposes (a data marketplace, an MCP server) calls that gateway from code with the gateway's own key, <GATEWAY>_API_KEY managed through Connections, never the provider behind it. When the gateway's HTTP API is unknown, read its documentation before writing the step; do not guess.

  • Engine features are used natively (see local.md): a hook or sleep is a wait node, a child workflow a sub node, a runAgent stage an agent node.

  • Reaching people: notify({ kind, text, approval?, target }) from lib/notify.ts posts to the GTM agent's notify route, which posts the text straight to Slack with the agent's bot token and no model call; tell and show are plain posts, ask and handoff open a message whose thread the agent watches, so a person's reply there wakes it. An approval in a stage notifies by itself when GTM_AGENT_URL and GTM_NOTIFY_SECRET are on the workflow project. Where it posts is code: const NOTIFY = { channelId: "C0…" } at the top of the workflow, given as notify: input.notify ?? NOTIFY to each stage and as target to each notify call; posts land top-level in that channel, never in the conversation a run was started from. Per-row news goes through runRows, never a notify call inside the step: notify: { target: input.notify ?? NOTIFY, every: "chunk", line: (row, columns) => … }, where every is row (one post per finished row), chunk (one post per run of rows, so one per child when fanned out; the default to propose) or run (one post with the totals at the end), a count and never a judgment; line returns a string, or { text, blocks } when a row deserves a rich post. notify: false on a stage keeps its approvals silent. The agent project's GTM_NOTIFY_CHANNEL is only the fallback when a workflow names none. A workflow calls the agent only to reach people, never to think.

  • Helpers are admitted to lib/ only when a bare SDK call was shown to fail or double-spend; everything else is a native SDK call with a worked example in patterns.

  • Routes, bearer GTM_RUN_SECRET: POST /api/run/<slug>, GET /api/run/<slug> (cron, also CRON_SECRET), POST /api/intake/<slug> (no bearer; the sender's signature), GET /api/runs/<id> (status, output, error, and approvals, pending first), POST /api/runs/<id>/approve with { token, approved, reason? }, GET /api/runs/<id>/stream (one JSON event per line while an agent stage streams), POST /api/runs/<id>/cancel, GET /api/link/<slug>. Base: http://localhost:3939 locally, GTM_WORKFLOW_URL for the deployed copy. When GTM_RUN_SECRET is the value host, the host supplies the credential outside the agent's reach and the call sends no Authorization header.

Outputs

workflows/<slug>.ts, its table and migration, registry entries, a vercel.json cron when scheduled, a diagram page, rows in the result table, and, once a project is connected, the deployed copy that every push refreshes.

For queue, provider, MCP, or CLI failures, read runtime reliability before attributing the cause or proposing a retry.

Exceptions

Requires the gtm-workspace skill installed alongside this one; when ../gtm-workspace/SKILL.md is missing, say: install it the same way this skill was installed, with npx skills add eliasstravik/gtm-skills -s gtm-workspace -y (add -g when this skill lives in the global skills directory), then retry. A missing key stops a run before it starts and is named. A production build on Vercel with no DATABASE_URL fails on purpose; report it and say the database is added through the Neon integration, as deploy.md describes. A run on the deployed copy needs a connected project; without GTM_WORKFLOW_URL, say how to connect one as Deploy does (shared standalone setup on a computer with the CLIs signed in; gtm-agent is optional) instead. A workflows/ that contains scripts/gtm.ts is the previous runtime, which this skill neither converts nor upgrades: say so, and offer to set it aside (remove workflows/, which stays in the workspace's history; its deployed copy keeps running until the next push) and scaffold fresh.

QC

  • Every workflow has a valid business graph whose labels, branches and results match its actual code. Business behavior changes update metadata in the same change. The metadata build passes; compiler graphs never substitute for missing business metadata.
  • Every step is the plainest kind that does its job: no AI step for deterministic work, no agent stage where plain and AI steps in a fixed order would do, unless the user named an agent.
  • A one-row test was offered, through the question tool, before the first run of more than one row of a new or changed workflow; a one-row request ran without that question; the cost statement preceded every run.
  • The result table has the four fixed columns; paid and AI steps have maxRetries = 0; no cached() or generateObject call sits in workflow scope.
  • Every agent stage is a runAgent() call in workflow scope with maxSteps, maxUsd, and timeout set; no WorkflowAgent import, no timeout: option, no AbortSignal.timeout, no .url() or .email() in a schema, no optional fields in an output schema; a new or changed agent stage was tested on one row and the reply named its tool calls, stop reason, and real cost.
  • Every workflow passes fanOut with itself, chunkSize 20 when it has an agent stage; a module a workflow imports never reaches the database outside a "use step" function; route-only code lives in *-api.ts files.
  • An ordinary Run reply ends with the result and repeats no entry unless its address changed or the user explicitly requested it; a save ends with what was created, changed, or deleted.

References

interactions dialogues; local.md for start, keys, data, schedules, tables, the viewer, the agent stage, fan-out, intake, notify, and the findings ledger; patterns for native SDK calls with worked examples; deploy.md; templates/workflows/example-scores.ts as the reference implementation and templates/workflows/example-research.ts as the agent-stage reference; from gtm-workspace: interaction, contract.

Files bundled with it

These load only when the skill asks for them, so they cost nothing until it runs.

Other skills for the same job

Different authors, same problem. Matched on the words in the skill name, across every library in the catalogue except this one.

Need help setting it up?

This page tells you what gtm-workflow does and what it needs. Cheetah builds the agent setup it runs inside: data, CRM, sequencing and the guardrails.

Book a call →

The directory stays free. There is nothing gated behind this.