# Tableau Workbook Auditor Find what is wrong with any Tableau workbook (`.twbx`), and map every dependency inside it, **entirely in your browser**. Unused fields, duplicate calculations, performance problems, full calculated-field lineage, filters, dashboards and stored SQL. Nothing is uploaded, stored, or sent anywhere. ๐Ÿ”— **Live:** https://tableau-lineage.com ๐Ÿ“ฆ **MCP server:** [`tableau-lineage-mcp`](https://www.npmjs.com/package/tableau-lineage-mcp) --- ## Why Inheriting someone else's Tableau workbook means reverse-engineering dozens of calculated fields by hand to answer one question: where does this number come from, and what breaks if I change it? Two more questions come up just as often: - **What in here is dead?** Tableau's Workbook Optimizer flags unused fields, but only from inside Tableau: the Server menu or publishing dialog in Desktop, or the publishing menu or publishing dialog in web authoring on Server or Cloud. - **Why is it slow?** Tableau's answers are that same Optimizer, which checks best-practice guidelines, and Performance Recording, which shows where the time goes. Both run only inside Tableau, and Tableau Catalog, which maps lineage, needs Data Management on Server or Cloud. This reads the workbook file itself: it answers the first two questions and flags the likely causes of the third, free, with no Tableau install, no licence and no account. Because workbooks often carry sensitive data, everything runs locally: the `.twbx` never leaves your machine, which is a property of the architecture rather than a promise (there is no server to send it to). ## Features ### Audit - **Dead weight**: every field, parameter and calculation nothing uses, graded by confidence (`unused`, `likely-unused`, `referenced-in-comment`) with the reason spelled out. Reported as candidates rather than certainties, because a workbook file cannot prove a field is unreferenced everywhere. - **Duplicate calculations**: identical formulas under different names, and the more dangerous case of the same name carrying different formulas. - **Performance lint**: 15 static rules over what the file actually contains. Heavy and nested LODs, long calculations, string-heavy work, too many quick filters, missing context filters, live connections, non-fixed dashboard sizing, dense dashboards, orphan worksheets. ### Lineage - **Interactive dependency graph** (vis-network): calculated fields, raw fields, parameters and worksheets as nodes; edges show what feeds what. Cluster by data source, search, highlight chains, zoom and pan. - **Searchable data dictionary**: every formula grouped by data source, plus a worksheets section. - **Seven clickable metrics**: data sources, calculated fields, raw fields, parameters, LOD calcs, table calcs, filters. ### Structure - **Filters, decoded**: every filter deduplicated across worksheets, with its kind (categorical / quantitative / relative-date), context-filter status, stored member selections and ranges, and a per-worksheet breakdown. Data-source filters are called out separately. - **Dashboards**: which worksheets each dashboard places, its fixed size and device layouts, plus sheets that sit on no dashboard and sheets that are hidden. - **Data provenance**: extract or live, connection class / database / server, published (`sqlproxy`) sources, and refresh history where the workbook records it. - **Stored SQL**: every Custom SQL query (full text), Initial SQL statement, stored-procedure reference with parameters, and `RAWSQL_*` calculation, each mapped to the connection it targets. Runtime-generated live-connection queries are not stored in workbook files, so they are explicitly out of scope. ### Compare versions Drop in up to five versions of the same dashboard and get a **semantic diff**: which calculations were added, removed, renamed or edited, and for every edit the downstream fields it can break, plus changed parameters, filters, worksheets, dashboards, data sources and SQL. Reformatting a calculation is not reported as a change, and a field that disappears while an identical formula appears under a new name is reported as a rename. A git diff of two `.twbx` files cannot do this: the workbook is one large XML blob where layout coordinates and regenerated ids swamp the handful of real changes. ### Exports All generated in-browser, from the same parsed model: - **Interactive HTML report**: self-contained and offline-ready (vis-network inlined), watermarked with the tool and the author. - **Markdown handover document**: the workbook written up (metrics, audit, dashboards, data sources, parameters, filters, every formula, stored SQL) for a ticket, a wiki or a README. Also the format an LLM reads best when someone asks about the workbook later. - **CSV** field inventory (including which worksheets use each field) and **JSON** of the complete model, audit included. ### Everything else - **Private by architecture**: `.twbx` parsing (unzip + XML) happens client-side. No backend, no upload, no cookies. Analytics is an anonymous, aggregate page-view count. - **Workbooks up to 500 MB**: only the workbook XML is decompressed; the packaged data extract is never touched. - **Accessible**: keyboard navigation, focus management, AA contrast, reduced-motion support, and a text dictionary as an equivalent to the canvas graph. - **Seven UI languages** for the core interface strings (buttons, labels, status messages), with English fallback. Longer explanatory copy is English only. ## How it works ``` .twbx (a ZIP) โ””โ”€ fflate unzips ONLY the .twb entry โ”€โ”€โ–บ DOMParser reads the XML once โ”œโ”€ extractor.ts lineage model: calcs, dependencies, params, stats โ”œโ”€ filterExtractor.ts filters + per-worksheet usage โ”œโ”€ sqlExtractor.ts Custom SQL, Initial SQL, stored procs, RAWSQL โ”œโ”€ dashboardExtractor dashboards, hidden/orphan sheets, provenance โ”œโ”€ audit.ts dead weight, duplicates, performance lint โ””โ”€ diff.ts semantic diff between two parsed workbooks โ”œโ”€ in-app: a sandboxed