Clay tables — explain one cell by walking a column backward through its dependencies and run-gate to the origin or root cause. Use when the user asks "where did this value come from?", "why did {action} error?", "why didn't {action} run?", or "why is this value empty?".
Scanned 9/3/2026
Install to Claude Code
npx -y skills add clay-run/agent-plugins --skill tables-value-trace --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Tables Value Trace?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/clay-run-tables-value-trace)More formats (shields.io, HTML) on the badges page.
---
name: tables-value-trace
description: Clay tables — explain one cell by walking a column backward through its dependencies and run-gate to the origin or root cause. Use when the user asks "where did this value come from?", "why did {action} error?", "why didn't {action} run?", or "why is this value empty?".
allowed-tools: Bash(clay *), Bash(jq *), Read
---
# Value-trace
**Use when:** you have a record and want to explain **one cell** — "where did this email come from?", "why did Enrich Person error?", "why didn't Push to HubSpot run?", "why is this value empty?". This walks a single column **backward** through its dependencies to find the origin or the root cause. Causation, not state — `/tables-trace` gives you the state snapshot first.
The method combines two things:
- **Structure** — column settings tell you the edges: who feeds whom (`{{f_xxx}}` tokens in `formulaText` / `inputsBinding` / `formulaMap`), and the run gate (`conditionalRunFormulaText`).
- **This row's data** — `clay tables rows get` tells you what each cell on _this specific row_ actually holds and its status.
Structure gives the edges; the row gives the values along them. You need both.
`{{f_xxx}}` references **never cross tables**, so the entire walk stays within the row's own table.
## 1. Anchor to the row and the target cell
You need this row and the target column. If you don't have the row yet, locate it first (`/tables-trace`, or a single value search — `tables query` when the table is query-enabled, else `rows list --filter`). Identify the **target column** — the one whose value, error, or non-run you're explaining — and its `f_id`. The explanation needs the row's full cell data — every cell's `status`, `value`, `fields`, and `error`; a `tables query` result already carries all of that, and `rows get` provides it when you hold a `rec_...` id (step 3).
## 2. Get the structure — `clay tables columns get`
On the table the row lives in. Build the dependency catalog with the token-extraction recipe in the tables entry-point skill's `dependency-catalog.md` — it yields `{ id, name, type, role, integration, gate, dependsOn }` per column. You only need to walk the target column and its transitive `dependsOn`, but computing the whole catalog once is cheap and lets you resolve every edge by name.
## 3. Get this row's data
You need every cell's `status`, `value`, `fields`, and `error` on this row. If you located the row via `tables query`, its result already carries all of that — use it directly. Otherwise (the `rows list` path, or you already hold a `rec_...` id), fetch it with `clay tables rows get`:
```bash
clay tables rows get <tableId> <rowId> | jq .
```
For any column you visit, read `status`, `value`, `fields`, and `error` (a message string) per cell — under `.cells["f_xxx"]` in a `rows get` result, or `["f_xxx"]` directly in a `tables query` row.
## 4. Walk backward from the target
Start at the target cell and follow `dependsOn` upstream, reading each upstream cell's actual state from the row, until you reach the cause (or a `basic`/`source` root). Branch on what the target cell shows:
### Explaining a value ("where did X come from?")
- **`basic` formula column** → the value is computed from `formulaText`; its `dependsOn` are the inputs. Read those upstream cells' values (`.value`), and recurse into any that are themselves derived, until you reach roots (`source` / input `basic`). Report the chain that produced the value.
- **`action` column** → the value is the action's output. Read its `fields` (the structured payload, populated on `rows get`) for what it actually returned, and its `inputsBinding` for what fed it; resolve each input's `{{f_xxx}}` to the upstream cell and show the value that went in.
### Diagnosing `error`
- Read `error` (the message string) on the target cell. Classify the cause (same patterns as `/tables-error-sweep` step 5: provider throttling, auth via `integration`/`authAccountId`, bad/missing input, provider rejection).
- Then check whether the cause is **upstream**: look at the cells this column `dependsOn`. If an input cell is empty, `error`, or hasn't run, the failure likely cascades from there — keep walking back. Distinguish "this action genuinely failed" from "this action got bad/no input because an upstream column did."
### Diagnosing `empty` (hasn't run / no value)
- **Check the gate first.** If the column has a `gate` (`conditionalRunFormulaText`), evaluate it against this row's actual cell values. A falsy gate is the usual reason an action sits at `empty` with no error — report the condition and which input made it falsy (e.g. "gated on `{{Domain}}`, and Domain is empty on this row"). (A gated skip can also surface as an `error` whose message names the run condition — treat that the same way.)
- **No gate, still empty** → an upstream input is `queued`/`running`/`empty`, so this column hasn't been reached yet. Walk back to find the first column that hasn't produced, and report that as the bottleneck.
### `queued` / `running` / `retry` / `rate_limited` / `awaiting_callback`
- Still processing (or waiting on an upstream that's processing). Note it and, if useful, point at the upstream cell it's waiting on.
## 5. Report — lead with the cause, then the chain
State the answer first (the origin, or the root cause), then show the backward chain that supports it with the actual per-cell values/statuses on this row.
**Value origin:**
```
"Enrich Person" → company "Acme Inc" came from Clearbit, keyed off the email.
Push HubSpot ← Enrich Person.company = "Acme Inc"
Enrich Person (Clearbit) ← Find Email = "jane@acme.com"
Find Email (Prospeo) ← Domain = "acme.com"
Domain (formula) ← Company = "Acme" (CSV import)
```
**Root cause of a failure / non-run:**
```
"Push to HubSpot" didn't run because it's gated on an email, and there isn't one.
Push to HubSpot empty gate: only if {{Find Email}} present
Find Email success but empty value ("no email found") ← Domain
Domain success = "acme.com"
Root cause: Find Email returned no email for this domain, so the HubSpot push
was correctly skipped. The fix is upstream (email discovery), not the push.
```
Keep the chain honest: only show edges the tokens actually establish, quote a gate exactly as its `conditionalRunFormulaText` reads, and ground each step in the value that cell really holds on this row.
## Hand-offs
- Want the full per-cell snapshot or to confirm where the record lives → `/tables-trace`.
- The same failure across many rows, not just this one → `/tables-error-sweep`.
- Understand the whole table's workflow, not one value → `/tables-analyze`.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!