---
name: building-gtm-tables
description: Turn a GTM idea into a fully configured, verified workflow table. Use this skill whenever the user asks to "build a table that..." / "set up an enrichment + scoring pipeline" / "add columns that run end-to-end against my rows." Blueprints the whole table in memory, validates composition, commits atomically in topological order, then runs single-row verify + a user-approved 2-5 row batch before declaring done. Do NOT use this skill for pure messaging or for editing an already-committed table one column at a time.
visibility: internal
---

# Building GTM Tables

You are the building-gtm-tables orchestrator. Your job is to turn a user's GTM
table idea into a validated workflow-table blueprint, commit that blueprint
atomically, and verify real rows before calling the work done.

## When to fire

Use this skill when the user asks for a net-new or substantially composed GTM
workflow table, for example:

- "Build a table that enriches LinkedIn profiles and scores each prospect."
- "Set up enrichment, ICP scoring, and an invite action for these rows."
- "Add columns that run end-to-end against my lead list."
- "Create a workflow table pipeline for pulling prospect context and deciding next action."
- "Turn this ICP idea into an automated table."

Do not use this skill for pure campaign messaging. Use create-campaign-v2 for
campaign creation and message approval flows. Do not use it for a simple
one-column edit after a table is already committed; call `sellable:add_column`
directly for that narrow case, or use `sellable:update_column`,
`sellable:delete_column`, and `sellable:reorder_columns` for explicit edits to
an existing table.

## Reasoning checklist

Before writing any blueprint JSON:

- Identify the row's inputs: LinkedIn URL, email, company domain, existing JSON,
  or another stable row field.
- Pick the enrichment recipe. The usual recipe is a `http_request` column named
  "Enrich Prospect" with endpoint `/api/v4/enrich-prospect`.
- Pick the scoring criteria. If AI ICP scoring is needed, use
  `score_icp_mcp` with a natural-language rubric the user can inspect. This
  is the MCP-safe AI scorer for new blueprints.
- Pick action or generation columns: LinkedIn actions, AI Column, or Generate
  Message.
- For LinkedIn DMs, choose the action path deliberately:
  `check_connection` -> `send_dm` for mixed or uncertain connection state,
  direct `send_dm` only when the table has reason to believe the sender is
  already connected, and `send_invite` -> accepted -> `send_dm` for cold
  prospects.
- Wire dependencies explicitly. Every downstream `inputMapping` key references
  the producer column by id.
- Read `references/brief-to-blueprint.md` for the full decomposition recipe and
  worked examples.
- Discover current column options with `sellable:list_column_types` and
  `sellable:get_column_schema` when you need exact config fields, defaults, or
  hidden/deprecated safety verdicts.

## Phase gates

Gate 1: BLUEPRINT.

Prerequisites: user intent is clear enough to identify row inputs, enrichment,
scoring, action/generation needs, and table id if updating an existing table.

What the agent does: author a complete in-memory Blueprint that conforms to
`core/blueprint-schema.json`; pick column types from
`references/column-type-catalog.md`; run the blueprint validator through
`sellable:commit_blueprint` dry-run behavior when available before mutation.

Exit criterion: the blueprint validator returns `ok:true`. Do not start COMMIT
until the blueprint validator returns `ok:true`.

Gate 2: COMMIT.

Prerequisites: Gate 1 passed and any user-visible tradeoff has been resolved.

What the agent does: call exactly one committing tool for the full table shape:
`sellable:commit_blueprint`.

Exit criterion: the committed table contains the intended columns, dependencies,
and orchestrator-queued cells in topological order.

Gate 3: VERIFY.

Prerequisites: Gate 2 succeeded and at least one representative row id is known.

What the agent does: run `sellable:verify_table_row` on one row, fix forward
until green, then run the required batch confirmation.

Exit criterion: do not declare DONE until BOTH single-row verify AND a
user-approved 2-5 row batch verify report `ok:true`; see Phase gate 3b below.
Single-row green alone is not sufficient.

## Blueprint phase (in-memory)

Author the Blueprint before touching live cells. The Blueprint object has
`tableId`, `columns`, and `edges`; each column has `id`, `type`, `name`,
`config`, optional `inputMapping`, optional `runCondition`, and optional
`deferExecutor`. Do not put top-level tool controls like `runCells` or
`validateOnly` inside blueprint columns; pass them to `sellable:add_column` or
`sellable:commit_blueprint` at the tool-call level.

Use `core/blueprint-schema.json` as the structured-output shape. Use
`references/column-type-catalog.md` to select current active column types and
exact display names. Use `references/brief-to-blueprint.md` to decompose the
brief into inputs, enrichment, scoring, action/generation, and wiring. Use
`references/failure-taxonomy.md` for validator checks framed as the error the
LLM likely made and the fix-forward action.

Blueprint rules:

- Use stable, readable column ids such as `linkedin_url`, `enrich_prospect`, and
  `score_icp_mcp`.
- Use registry type keys exactly, not display names.
- Treat the API column registry as the source of truth for type validation.
  MCP code must not maintain its own hardcoded column-type allowlist; if the API
  returns `phantom_type`, `deprecated_type`, or `not_createable_type`, fix the
  blueprint to match the current registry response.
- Treat "Enrich Prospect" as a `http_request` preset, not its own type.
- Use `score_icp_mcp` for new AI ICP scoring. Do not use legacy
  `score_icp` or the old `score_icp_rubric` alias; they are reserved for older
  saved tables and different config contracts.
- Treat Enrich Prospect as the baseline Sellable producer for prospect context.
  If a blueprint includes `score_icp_mcp` or `generate_message`, it should
  also include `http_request` named "Enrich Prospect" unless the user explicitly
  says the table already has an enriched prospect id column.
- Put every dependency in `inputMapping`; do not rely on implicit column-name
  guessing.
- When a downstream column needs an enriched prospect, wire it to the Enrich
  Prospect root `id` field, not to the whole HTTP result:
  `score_icp_mcp.inputMapping.enrichedProspectId -> enrich_prospect` and
  `generate_message.inputMapping.enrichedProspectId -> enrich_prospect`.
  The commit tool materializes that blueprint reference as
  `{{<actual Enrich Prospect column id>.id}}`.
- Keep `edges` aligned with `inputMapping` so humans can inspect the graph.
- Prefer topological order in the `columns` array even though the commit tool
  validates and commits in dependency order.
- DM/connection rules:
  - Every LinkedIn action table needs clear sender context from the table,
    campaign, or user request.
  - Treat `send_dm` as an existing-connection action. Use it only when we have
    connection evidence: the user supplied an existing-connection list, a
    positive `check_connection`, or accepted invite proof from the current flow.
    The runtime still checks the sender's connection proof and fails with
    `errors/not_connected` instead of sending when the sender is not connected.
  - For a mixed or uncertain list, add `check_connection` before `send_dm` and
    branch/gate the DM to connected rows. A positive `check_connection` can pin
    scheduling to the proven `connectedSenderId`.
  - Do not treat generic prior same-sender outreach as connection proof. Use
    synced sender connections, a positive `check_connection`, or accepted invite
    proof from the current flow.

Config rules:

- Put native column settings in `config`. The commit path preserves full config
  objects and validates them server-side before mutation.
- Use blueprint-level `inputMapping` for references to existing producer
  columns. The commit path materializes those references into production
  template mappings such as `{{<actual column id>}}` or
  `{{<actual Enrich Prospect column id>.id}}`.
- Use `runCondition` for branch gates that should exist in production. Template
  tokens in `config`, AI prompts, formula expressions, and `runCondition` are
  dependency-bearing and must point to real producer columns.
- For outbound HTTP throttling, set `http_request.config.rateLimit` as either
  `{ "mode": "window", "maxRequests": N, "windowSeconds": S }` or
  `{ "mode": "concurrency", "maxConcurrent": N }`.
- For public webhook intake throttling, set
  `inbound_webhook.config.rateLimit` as
  `{ "maxRequests": N, "windowSeconds": S }`. Omit it to use the server default.
- Do not invent per-column `rateLimit` for LinkedIn action columns. Those are
  governed by sender/account limits and scheduling windows; configure action
  behavior with `actionConfig`, `waitForEvent`, `runCondition`, and producer
  mappings.

## Commit phase

Use exactly one tool for the initial commit:

`sellable:commit_blueprint({ tableId, blueprint, runCells?, validateOnly? })`

Use `validateOnly:true` for dry-run validation. Use `runCells:false` when the
user wants save-only column creation; omitted `runCells` preserves backend
default dispatch behavior.

Retry up to 3 times for recoverable errors:

- `phantom_type`: replace the invalid type with the active registry key.
- `required_field`: fill the missing config or input mapping.
- `wrong_order`: move the producer earlier and fix the graph.
- `missing_producer`: add the producer column or correct the referenced id.
- `concurrent_commit`: wait briefly, reload table context if needed, and retry.

Escalate after 3 retries with the last validator response and the smallest
concrete question needed to unblock the user.

## Verify phase

Read `references/verify-loop.md` before declaring done.

### Phase gate 3a: Single-row verify

Run:

```json
sellable:verify_table_row({ "rowId": "ROW_ID", "skipColumnIds": [], "forceRerun": true })
```

Expect `ok:true` with per-column statuses among `success`, `skipped`,
`deferred_to_sweeper`, and `condition_not_met`. Any `error` means fix forward:
adjust blueprint-derived column config with the supported table tooling, rerun
verify, and escalate after 3 attempts on the same column.

### Phase gate 3b: Batch confirmation (2-5 rows user-approved)

After single-row green, ask the user to pick 2-5 additional rows. Re-run
`sellable:verify_table_row` for each selected row. Declare done ONLY if all rows
return `ok:true`. Single-row green alone is NOT sufficient to declare the skill
done.

### Skipped columns policy (option d)

Wait and long-timer columns are skipped via caller-supplied `skipColumnIds`.
This skill does not override `runCondition` at runtime. Runtime condition
swapping is brittle because it requires a restore step and can hide the actual
production behavior.

## Tool inventory

- `sellable:commit_blueprint`: validate and atomically commit the full table
  blueprint in dependency order.
- `sellable:add_column`: add one column to an already-committed table when the
  user explicitly asks for a narrow column edit outside this full-table skill.
- `sellable:list_column_types`: discover visible createable column options, with
  optional hidden/deprecated rows for existing-table safety. The only arguments
  are `includeHidden` and `includeDeprecated`.
- `sellable:get_column_schema`: inspect one column option, including default
  config, editor contract, mutation capabilities, editable fields, full
  replacement config mode, run behavior, and safety verdict.
- `sellable:update_column`: update an existing column's name or full replacement
  config after checking `sellable:get_column_schema`; use `runCells:true` when
  the schema says edit reprocessing is supported.
- `sellable:delete_column`: delete a column through backend cleanup and inspect
  affected dependencies, cancelled cells, and cleanup warnings.
- `sellable:reorder_columns`: submit the exact full column ID order for a table.
- `sellable:verify_table_row`: execute or inspect a row through the committed
  table graph and return per-column verification statuses.

## HARD INVARIANTS - forbidden direct tool calls

HARD INVARIANT: Never call `sellable:queue_cells` directly. Use
`sellable:commit_blueprint` to create columns, which queues their cells
automatically through the orchestrator, and `sellable:verify_table_row` to
trigger verification runs. Direct queue_cells calls bypass
blueprint-then-commit discipline and produce the premature-queueing bug class
documented in `references/failure-taxonomy.md`.

HARD INVARIANT: Never call `sellable:update_cell` directly. Cell values are
populated by column execution, not user-authored. Direct update_cell calls hide
broken wiring that `sellable:verify_table_row` would catch.

HARD INVARIANT: Never invent a tool name such as configure_column or
update_column_run_condition. Use only registered column tools:
list_column_types, get_column_schema, commit_blueprint, add_column,
update_column, delete_column, reorder_columns, and verify_table_row.

## Failure taxonomy

Read `references/failure-taxonomy.md` when validation fails, when a tool call
looks tempting before the blueprint is complete, or when the table verifies on
one row but not a batch.

## Progressive disclosure index

- `references/column-type-catalog.md`: read when choosing type keys, exact
  display names, input mappings, output expectations, and when not to use each
  column.
- `references/brief-to-blueprint.md`: read when translating the user's brief
  into the in-memory blueprint reasoning trace.
- `references/verify-loop.md`: read before single-row verify, batch
  confirmation, skipped-column handling, or declaring done.
- `references/common-blueprints.md`: read for complete JSON examples you can
  adapt.
- `references/uat-seed-prompts.md`: read when running live UAT against a real
  table or rehearsing seeded validator faults.
- `references/failure-taxonomy.md`: read when the validator reports an error or
  when the agent is tempted to call direct cell tools.
