← Files TableauARCHIVED FILE

skills/tableau-workbook-authoring/references/field-edits.md

10.6 KB · Oct 3, 2026 · 06:23 UTC

↓ Download file

# Field-level workbook edits

Read this when a request needs one small, mechanical change to an existing
worksheet that the chart catalog doesn't cover as a whole template — add a
breakdown/color split, or add a filter — rather than a brand-new chart. Full
CLI flags and failure recovery for `scripts/tableau_resources.py`'s
`inspect-workbook`, `add-encoding`, and `add-filter` (`scripts/` relative to
the plugin root, same as `validate_workbook.py`).

These commands splice one bounded, verified XML shape into an existing
worksheet — they never invent a new chart type or restructure a view. Each is
deliberately scoped to the field types this module has confirmed against
real Tableau donor XML; anything outside that scope fails closed with a
message pointing back to a hand-edit (see
[`xml-troubleshooting.md`](xml-troubleshooting.md)) instead of guessing at an
unverified shape.

## Workflow

1. Resolve and download the workbook per `SKILL.md`'s "Existing workbook,
   first pass this task" — you need the extracted `.twb` before any of this.
2. Run `inspect-workbook` to see the exact field names, datatypes, and roles
   the target datasource declares. Never guess a field name from the user's
   phrasing — a prompt like "broken down by location" may not match the
   workbook's field name exactly (e.g. `Location` vs `Store Location`); if
   more than one field plausibly matches, ask the user with
   `request-user-input` rather than picking one.
3. Add the encoding and/or filter the user asked for with `add-encoding`
   and/or `add-filter`. Each command validates its own output before writing
   — see each section below.
4. Continue with `SKILL.md`'s "Validate and publish" section.

## Quick reference

```bash
python3 scripts/tableau_resources.py inspect-workbook --input <file> --format text
python3 scripts/tableau_resources.py add-encoding \
  --input <file> --output <file> --worksheet <name> --field <name> [--channel color|tooltip|label]
python3 scripts/tableau_resources.py add-filter \
  --input <file> --output <file> --worksheet <name> --field <name> \
  --filter-type categorical --include <value> [--include <value> ...]
python3 scripts/tableau_resources.py add-filter \
  --input <file> --output <file> --worksheet <name> --field <name> \
  --filter-type quantitative --min <value> --max <value>
```

## `inspect-workbook` — read real field names before mapping

```bash
python3 scripts/tableau_resources.py inspect-workbook --input ./workbook.twb --format text
```

Returns each datasource the workbook's own `<datasources>` container
declares, with every field's datatype and role (`dimension`/`measure`).
Fields are read from both layers a real workbook may use — an explicit
`<column>` declaration and a physical `<metadata-record>` — so this also
surfaces columns the author never customized. `--format json` (the default)
returns the same data machine-readably.

This is read-only: it never writes a file, and it works on any `.twb`, not
only one this module produced.

## `add-encoding` — add a color, tooltip, or label field to a pane

```bash
python3 scripts/tableau_resources.py add-encoding \
  --input ./workbook.twb --output ./workbook-with-breakdown.twb \
  --worksheet "ARR Trend" --field Location

python3 scripts/tableau_resources.py add-encoding \
  --input ./workbook.twb --output ./workbook-with-tooltip.twb \
  --worksheet "ARR Trend" --field "Renewal Date" --channel tooltip
```

Splices a `<color column='[ds].[field-instance]' />`, `<tooltip ...>`, or
`<text ...>` (`--channel label` maps to Tableau's own `<text>` tag) into the
target worksheet's single `<table><panes><pane><encodings>`, declaring the
field's `<column>`/`<column-instance>` dependency first if the worksheet
doesn't already carry one for it.

- `--channel` defaults to `color`; `tooltip` and `label` are also supported.
  `size`/`shape` are not implemented — no bundled donor XML verifies their
  column-instance shape.
- `color` requires a **string dimension** field, per `inspect-workbook`. A
  numeric measure, a date, or a boolean field is refused for `color` — hand-
  edit the worksheet for those instead.
- `tooltip`/`label` additionally accept a **numeric or date/datetime**
  field, verified against bundled donor XML (a candlestick chart's stacked
  `<tooltip>` elements, a treemap's stacked `<text>` elements) — a boolean
  field is still refused for every channel.
- The worksheet must have exactly one pane (a trellis/dual-axis worksheet
  with more than one pane is refused — which pane the user meant is
  ambiguous) and exactly one `<datasource-dependencies>` block (a blended,
  multi-datasource worksheet is refused for the same reason).
- A mark has exactly one color, so if the pane already has a color encoding,
  `add-encoding --channel color` refuses rather than guessing whether to
  replace it — remove the existing one by hand first. `tooltip`/`label` are
  additive instead: adding a second (or third) tooltip/label field stacks it
  alongside any that already exist, matching Tableau's own behavior for
  those two channels.
- Only `<encodings>` is touched. A worksheet's `<window><cards>` shelf layout
  (which panel the authoring UI shows) is left as-is; Tableau resynthesizes
  it from the pane's encodings the next time the workbook is opened in
  Desktop, and the rendered view does not depend on it. Confirm the rendered
  color legend/tooltip/label looks right during the render step regardless.

## `add-filter` — add a categorical or range filter to a worksheet

```bash
# Categorical: one or more values to include
python3 scripts/tableau_resources.py add-filter \
  --input ./workbook.twb --output ./workbook-filtered.twb \
  --worksheet "ARR Trend" --field "Admission Type" \
  --filter-type categorical --include Readmission

# Quantitative: a numeric or date range
python3 scripts/tableau_resources.py add-filter \
  --input ./workbook.twb --output ./workbook-filtered.twb \
  --worksheet "ARR Trend" --field "Length of Stay" \
  --filter-type quantitative --min 1 --max 30
```

Splices a `<filter>` immediately after the target worksheet's
`<datasource-dependencies>` in `<table><view>` — the same position bundled
donor `.tbm` files use — declaring the field's dependency first if needed.

- `categorical` requires one or more `--include` values and a **string
  dimension** field. A single value becomes a bare
  `<groupfilter function='member'>`; more than one wraps each value's
  `member` groupfilter in a `function='union'` groupfilter. Both shapes are
  verified against bundled reference `.tbm` XML. There is no `--exclude` —
  Tableau's exclude-filter shape isn't verified here, so an exclude filter
  needs a hand-edit.
- `quantitative` requires both `--min` and `--max` and a **numeric, date, or
  datetime** field. A numeric field's `--min`/`--max` must be plain numbers
  (`-?\d+(\.\d+)?`); a date field's must be `YYYY-MM-DD`; a datetime field's
  may be either `YYYY-MM-DD` or `YYYY-MM-DD HH:MM:SS`. Any of these is
  written as a `#...#` literal (Tableau's date/datetime-literal delimiter,
  e.g. `#2025-06-30 17:30:00#`). Only an `in-range` (both bounds) filter is
  supported — a one-sided or `non-null`-only filter needs a hand-edit.
- Relative-date filters (e.g. "last 90 days") and boolean-field filters
  aren't implemented — neither has a bundled donor example this module could
  verify the exact attribute shape against.
- Adding a second filter on a field that already has one is allowed; this
  does not check for or dedupe against existing filters.

## Datatype/role scope, at a glance

| Operation | Requires | Instance shape used |
| --- | --- | --- |
| `add-encoding --channel color` | string, `role=dimension` | `[none:<field>:nk]`, `type='nominal'` |
| `add-encoding --channel tooltip\|label` | string, numeric, or date/datetime | `[none:<field>:nk]` or `[none:<field>:qk]` |
| `add-filter categorical` | string, `role=dimension` | `[none:<field>:nk]`, `type='nominal'` |
| `add-filter quantitative` | numeric, date, or datetime | `[none:<field>:qk]`, `type='quantitative'` |

Both commands reuse an existing `derivation='None'` column-instance for the
field when the worksheet's `<datasource-dependencies>` already declares one
(matching by the underlying field, not by name), so a field the worksheet
already uses elsewhere doesn't get a redundant second declaration.

## Failure recovery

| Error | Cause | Fix |
| --- | --- | --- |
| `Workbook has no <worksheet> named ...` | `--worksheet` doesn't match any worksheet in the input | Re-check the name against `download-workbook`'s content or the raw XML |
| `Workbook has N <datasource-dependencies> blocks; expected exactly one` | The worksheet blends more than one datasource | Not supported — hand-edit instead |
| `Workbook has N <pane> elements; expected exactly one` | The worksheet is a trellis/dual-axis view with multiple panes | Not supported — hand-edit the specific pane instead |
| `Datasource <name> has no field named ...` | `--field` doesn't match a declared field | Run `inspect-workbook` for the exact name |
| `... only supports a string dimension field; <field> is <datatype>/<role>` | `--field` is the wrong type for `add-encoding --channel color` or a categorical `add-filter` | Use a string dimension field, or hand-edit for another type |
| `... only supports a string, numeric, or date/datetime field; <field> is <datatype>` | `--field` is the wrong type for `add-encoding --channel tooltip\|label` | Use a string, numeric, or date/datetime field, or hand-edit for another type |
| `... only supports a numeric or date field; <field> is <datatype>` | `--field` is the wrong type for a quantitative `add-filter` | Use a numeric, date, or datetime field, or hand-edit for another type |
| `Pane already has a color encoding; remove it by hand ...` | The worksheet already has a color breakdown | Remove the existing `<color>` encoding by hand first, or pick a different worksheet — `tooltip`/`label` don't hit this, they stack instead |
| `Workbook already declares a column-instance named [...] for a different field` | An unrelated field already uses the exact instance name this would synthesize | Rename or hand-edit; this refuses to reuse a name it didn't create for this field |
| `--min/--max must be plain numbers ...` / `--min/--max must be YYYY-MM-DD ...` / `--min/--max must be YYYY-MM-DD or YYYY-MM-DD HH:MM:SS ...` | Range bounds don't match the target field's datatype | Fix the literal format |
| `Generated workbook failed validation: ...` | The edit would introduce a new structural error | This is a delta check like `inject`'s — only *new* errors block; read the listed codes |
| `Refusing to replace existing file ...` | `--output` exists and `--overwrite` was omitted | Pass `--overwrite` or choose a different `--output` |

SHA-256: 1fce387b6f8b9d2a9605cbeeea78e50804414f20324e9fa1a59055d03771d87a