feat(tables): Add enrichment table column type#4752
Conversation
Add a Clay-style enrichments catalog to the table view and wire per-row input mapping into workflow-backed columns. - New "Enrichments" entry in the New-column dropdown opens a sliding panel listing curated enrichment templates; picking one swaps to the workflow config in-place (no cross-slide) with a back button. - Type the workflow sidebar as manual | enrichment; enrichment hides the launch + add-column-inputs affordances. - Add a "Workflow inputs" advanced panel mapping Start-block input fields to table columns (left-of-workflow columns only), with name-match auto-fill and collapsible input-mapping-style rows. - Persist type + inputMappings on the workflow group (types, contract, route, service, hook) — jsonb, no migration. - Consume inputMappings at run time: when present, feed Start-block fields from the mapped columns; otherwise fall back to name-match spread. - Clean up inputMappings on column rename/delete (stripGroupDeps + renameColumn). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pull the collapsible field-card markup (surface-4 header + surface-2 body, click/keyboard toggle, truncated title + optional badge) into a shared `CollapsibleCard` emcn component, and use it in the workflow-builder input mapping rows and the table sidebar's input-mapping panel. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Enrichments are now TS configs in apps/sim/enrichments/ (registry, like
connectors) that run directly per table row via the existing run/dispatch/
cell-write rails — no workflow execution.
- enrichments/{types,registry} + work-email (heuristic) and phone-number (stub).
- WorkflowGroup gains enrichmentId; WorkflowGroupOutput gains outputId
(workflowId/blockId/path kept required, '' for enrichment groups).
- Executor branches on group.type === 'enrichment' → maps inputMappings →
enrich() → outputs by outputId → cell-write. Missing required inputs skip
(blank cell) instead of erroring.
- Sidebar lists the registry; enrichment-config panel maps inputs to columns
and creates the enrichment group (no workflow UI).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace each enrichment's single enrich() with an ordered providers[]
fallback cascade. Providers are plain data ({ id, label, toolId,
buildParams, mapOutput }) so the catalog stays client-safe; the
server-only runner (run.ts) calls executeTool per provider, first
non-empty result wins, misses/errors fall through, all-miss = blank cell.
Wire four enrichments on the hosted-safe providers (Hunter, PDL):
- Work Email (fullName, companyDomain): Hunter -> PDL
- Phone Number (fullName, companyDomain): PDL
- Company Domain (companyName): PDL
- Company Info (domain): PDL -> Hunter
Person enrichments take a single canonical fullName (Clay-style); Hunter
gets first/last via splitName(), PDL takes name directly.
Add 'enrichment' to usage_log_source enum (+ migration) so hosted-key
tool cost from these per-row calls can be billed to the table owner.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rt safety
- runEnrichment now returns { result, cost, error }: accumulates hosted-key
cost across the cascade, and sets `error` only when every provider that ran
errored (auth/rate-limit/outage) vs a clean miss.
- Executor records the cost to the table owner (createdBy) via recordUsage
(source 'enrichment'); billing failures are logged, never error the cell.
- F1: all-providers-errored now writes status 'error' instead of a blank
'completed' cell that looked like "no data found".
- F2: re-check the abort signal after the cascade so a cancel mid-tool-call
isn't recorded as a completed empty cell.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Meta-header shows the enrichment's name + icon (Mail/Phone/Globe/Building2) instead of "Workflow" + a color chip. - Per-column header icon uses the enrichment's icon (via columnSourceInfo) instead of the generic play icon. - Hide "View execution" for enrichment cells in both the row context menu and the action bar (no workflow execution exists to open); also hide the meta-menu "View workflow" item for enrichment groups. - Clicking an enrichment column header now opens the enrichments sidebar in edit mode (pre-filled input mappings, Update via useUpdateWorkflowGroup) instead of the workflow "Configure workflow" sidebar. - Enrichment config lets the user name each output column (editable per-output, deduped defaults) since enrichments can produce multiple columns. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Drop the per-column enrichment icon (it duplicated the meta-header icon). Enrichment output columns now render the standard column-type icon (Text, etc.) — the enrichment's icon stays only on the group meta-header. - Make output column names editable in the enrichment config edit mode too; changed names rename their columns via useUpdateColumn (the rename cascades into the group's output refs server-side). Validation excludes the output's own current name. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Edit column on an enrichment output now opens the normal column-config sidebar (rename / type / unique) instead of the workflow 'Configure output column' panel, which showed workflow-only fields and blocked a simple rename. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Let the copilot enumerate the code-defined enrichment registry and add an enrichment column to a table (validating required input mappings against the table's columns), backed by the same workflow-group machinery the UI uses.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryHigh Risk Overview A new Table UX: + New column → Enrichments opens a catalog sidebar and config (input mapping, output column names, auto-run deps). Groups persist Copilot catalog gains Reviewed by Cursor Bugbot for commit f859a0c. Bugbot is set up for automated code reviews on this repo. Configure here. |
Greptile SummaryThis PR introduces code-defined enrichment columns for tables: a TypeScript registry (
Confidence Score: 2/5Not safe to merge until the hardcoded The apps/sim/lib/core/config/feature-flags.ts — the hardcoded
|
| Filename | Overview |
|---|---|
| apps/sim/lib/core/config/feature-flags.ts | isHosted hardcoded to true — a debugging artifact that causes every deployment to inject Sim's hosted API keys for PDL/Hunter enrichment calls. |
| apps/sim/enrichments/run.ts | New enrichment runner with provider cascade logic; 404 is treated as a clean no-match (correct), cost is accumulated correctly for successful calls. |
| apps/sim/background/workflow-column-execution.ts | Adds enrichment execution path alongside the existing workflow path; correctly skips own output columns from input mapping, handles abort signals, and wraps billing failures so they don't error the cell. |
| apps/sim/lib/table/service.ts | Cascades column renames into inputMappings and uses outputId-keyed diff for enrichment outputs; correct output key fallback logic. |
| apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/enrichments-sidebar/enrichment-config.tsx | New enrichment config panel with input mapping, output column naming, and auto-run settings; handles both create and edit modes. |
| apps/sim/lib/copilot/tools/server/table/user-table.ts | Adds list_enrichments and add_enrichment copilot operations; validates required inputs, column existence, and output column naming before calling addWorkflowGroup. |
| packages/db/migrations/0213_wealthy_sue_storm.sql | Adds enrichment to the usage_log_source enum; migration is correct and schema.ts is kept in sync. |
| apps/sim/tools/index.ts | Surfaces HTTP status on the error output object so run.ts can distinguish 404 no-match from auth/rate-limit failures; targeted and correct change. |
Sequence Diagram
sequenceDiagram
participant Scheduler
participant CellExecutor as workflow-column-execution
participant RunEnrichment as enrichments/run.ts
participant ExecuteTool as tools/index.ts
participant Provider as PDL / Hunter API
participant UsageLog as billing/usage-log
Scheduler->>CellExecutor: runWorkflowAndWriteTerminal(group)
CellExecutor->>CellExecutor: "group.type === enrichment?"
CellExecutor->>CellExecutor: map inputMappings to enrichInputs
CellExecutor->>CellExecutor: check missingRequired, skip row if true
CellExecutor->>RunEnrichment: runEnrichment(enrichment, inputs, ctx)
loop Provider cascade (first-result-wins)
RunEnrichment->>ExecuteTool: executeTool(toolId, params + workspaceId)
ExecuteTool->>Provider: HTTP request (BYOK or hosted key)
Provider-->>ExecuteTool: response
ExecuteTool-->>RunEnrichment: success, output, error
alt 404 clean miss
RunEnrichment->>RunEnrichment: continue to next provider
else success + non-empty result
RunEnrichment-->>CellExecutor: result, cost, error null
else error
RunEnrichment->>RunEnrichment: errorCount++ try next
end
end
RunEnrichment-->>CellExecutor: result empty, cost, error
alt "cost > 0"
CellExecutor->>UsageLog: recordUsage(enrichment cost)
end
CellExecutor->>CellExecutor: writeState completed or error with dataPatch
Reviews (3): Last reviewed commit: "fix lint" | Re-trigger Greptile
- Guard the enrichment cell path on `enrichmentId` so a group typed 'enrichment' without a registry id falls through to the workflow path instead of erroring. - Clear stale output values when skipping a row for missing required inputs, so the auto cascade re-enriches once inputs return (was left completed+filled). - Write a terminal state on abort in the enrichment path (matches the workflow path) so a cancel between run and terminal-write can't leave the cell running. - Edit mode: apply the group update (mappings/deps/auto-run) before column renames so the primary edit lands even if a rename fails. - Disable Save once validation has surfaced a missing required input. - Use the workflowGroupById map instead of O(n) find in the context-menu and action-bar hot paths.
|
Addressed review feedback in Cursor Bugbot
Greptile
tsc + biome clean. |
|
@greptile review |
Guides adding a code-defined table enrichment to the registry, with a required step to verify each provider tool has hosted-key support and chain to /add-hosted-key when it doesn't.
- updateWorkflowGroup output diff now keys on outputId (falling back to blockId::path) so enrichment outputs — which share empty blockId/path — no longer collapse to one key and drop sibling columns. - Enrichment terminal write now clears output columns absent from the result, so a partial/empty re-run doesn't leave stale values. - Editing a group whose enrichment was removed from the registry shows an explanatory panel instead of silently falling through to the new-enrichment catalog.
|
Addressed the second-pass review in `61343a11a`:
tsc + biome clean. |
…ells An enrichment that runs to completion but matches nothing now renders a gray "Not found" badge (like the Queued/Waiting cell states) instead of a blank cell, so a real miss is distinguishable from an unrun cell. Scoped to enrichment output columns; an empty string no longer counts as a value.
| if (!isNull) return { kind: 'value', text: stringifyValue(value) } | ||
| // while other blocks in the group are still running. An empty string is not | ||
| // a value — it falls through so a completed enrichment can show "Not found". | ||
| if (!isEmpty) return { kind: 'value', text: stringifyValue(value) } |
There was a problem hiding this comment.
Empty-string workflow outputs now show incorrect running state
Medium Severity
Changing the guard from !isNull to !isEmpty affects all workflow-group columns, not just enrichment ones. When a regular workflow block finishes with an empty string output while other blocks in the group are still running, the cell now falls through to the inFlight branch and incorrectly renders as "queued" or "pending-upstream" instead of showing the completed (empty) value. The isEnrichmentOutput guard for "Not found" only helps enrichment columns — regular workflow columns with legitimate '' outputs regress to showing an active-running indicator after the block has finished.
Reviewed by Cursor Bugbot for commit 4e7b751. Configure here.
… cascade A completed enrichment with empty outputs is a real no-match result, not an unfinished run. Eligibility now treats an enrichment's completed status as terminal (regardless of output fill), so the auto cascade stops re-invoking billable provider calls on every no-match row each dispatch. Input changes still clear the exec entry, so genuine re-runs are unaffected; manual Run all still re-runs.
|
Latest review pass:
Also shipped `4e7b75196`: completed-but-empty enrichment cells render a "Not found" badge instead of a blank. tsc + biome clean. |
Providers like People Data Labs signal 'no record found' with HTTP 404, which executeTool surfaces as a failed ToolResponse (status on output.status). The cascade now treats a 404 as a clean miss — falls through to the next provider and lets the cell render 'Not found' — instead of marking the cell errored. Auth/rate-limit/5xx still propagate as real errors.
executeTool's catch handled Error instances in its first branch and only extracted status/statusText/data for non-Error object throws — so HTTP errors (thrown as Error instances carrying .status) lost their status on the returned output. Surface it for Error instances too, so callers can branch on the status (e.g. the enrichment cascade treating a provider 404 as a no-match).
|
@greptile review |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f859a0c. Configure here.


Summary
apps/sim/enrichments/) that runs directly per table row on the existing workflow-group rails — no workflow neededexecuteToolwith hosted-key/BYOK injection. v1: Work Email (Hunter→PDL), Phone (PDL), Company Domain (PDL), Company Info (PDL→Hunter)recordUsage(newenrichmentusage source + migration); all-providers-errored surfaces an errored cell; abort-safelist_enrichments+add_enrichmenttools so Mothership can add enrichment columnsType of Change
Testing
Tested manually;
tscclean,biome checkclean,bun run check:api-validation:strictpasses, copilot tool tests pass (13/13)Checklist