# Tables Value Trace

> 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?".

- Skill: `clay-run/tables-value-trace` (Agent Skill)
- Install (CLI): `npx skillmds@latest add clay-run/tables-value-trace`
- Raw SKILL.md: https://api.skillmd.com/api/skills/clay-run/tables-value-trace/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: clay-run (https://skillmd.com/u/clay-run)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/clay-run/tables-value-trace

---


# 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 `r_...` 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 `r_...` 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`.

