Prop 40, BSZ analysis: the materials

Every script, test, data file and source behind the explorer, the report and the slides

Published

October 14, 2026

This page is a guide to the Open Materials layer (the repository) of the BSZ analysis (Boll, Saez and Zucman, BSZ, plus the Galle, Gamage, Saez and Shanske (GGSS) expert report): what to download, what to run, and what the checks found. Everything lives in one repository, fhoces/opa-prop40, under bsz-analysis/. The tables on this page are built from the files themselves when the page is rendered, so they cannot fall out of date with the code without a re-render showing it.

Rebuild everything

git clone https://github.com/fhoces/opa-prop40 && cd opa-prop40/bsz-analysis
curl -L -o original-materials/BSZ_MainTablesFigures.xlsx \
     https://eml.berkeley.edu/~saez/BSZ_MainTablesFigures.xlsx   # check the SHA-256 below
Rscript -e 'targets::tar_make(names = !tidyselect::any_of(c("report_html","report_pdf")))'
Rscript tools/site-test-results.R        # the full test suite; writes site/data/test-results.csv
cd site && quarto render repro.qmd && quarto render materials.qmd
cd slides && Rscript -e 'rmarkdown::render("slides.Rmd")'

The pipeline is an R {targets} project. The default workbook vintage (the dated version of the workbook a number comes from) is August 2026; BSZ_VINTAGE=may switches every vintage-dependent path, column and constant to May. CAWTBSZ_MATERIALS points the pipeline at the sources if they are not in original-materials/.

Every computation also has a Python twin in py/, with the same function names as in R/. After pip install -r requirements.txt, the command python py/run_export.py (about 2 seconds) writes the same results to export/py/, and tests/testthat/test-py-parity.R checks each file against R’s. The report’s code folds show both languages.

Tests

file blocks passed failed skipped
test-compute.R 9 216 0 0
test-data_sheets.R 9 45 0 0
test-figures.R 22 76 0 0
test-ingest_excel.R 5 21 0 0
test-py-parity.R 2 56 0 0
test-site.R 7 48 0 0
test-snapshots.R 28 34 0 0
test-sql-steps.R 6 18 0 0
test-sql-workbook.R 3 41 0 0
test-sql.R 3 38 0 0
test-tables.R 14 121 0 0
test-verify.R 6 7 0 0
total 114 721 0 0

Last run: 2026-10-14, August vintage: 721 expectations passed, 0 failed, in 114 test blocks. Of these, 673 belong to the reproduction itself and 48 to this site: they check that the explorer reproduces Table 5 to 1e-12, that explorer/grid.js is the exporter’s own output, and that every number typed into a hand-written page still matches the pipeline. GitHub Actions rebuilds the pipeline and runs the suite on every push that touches bsz-analysis/ (.github/workflows/bsz-ci.yml); it does not render the Quarto pages or the slides.

Scripts

file lines what it does
R/code_listing.R 24 helper for code listings in the original report
R/compute_billionaires_ca_inctax.R 736 CA income tax of all billionaires (FTB + Pareto), and all taxes
R/compute_data_sec_agg.R 46 yearly totals from the billionaire list (drops the duplicated rows)
R/compute_pareto.R 163 Pareto extrapolation of missing billionaires; the Laffer curve
R/compute_shortrunseries.R 282 wealth growth panel and the 2025 snapshot
R/compute_tab5.R 144 Table 5: the estimating function the explorer also runs, and the four estimate rows of the one-time tax
R/compute_top4taxes.R 133 taxes of the top 4 by year, 2004-2025
R/data_sheets.R 193 14 extractors, one per workbook sheet the pipeline reads
R/excel_cells.R 22 single-cell and row/column readers shared by the compute files
R/export_contract.R 96 Export contract: the only files the comparison layer (comparison/) reads from
R/figures.R 725 the twelve ggplot figures
R/form4_basis.R 132 R twin of py/form4_basis.py: capital gains on Form 4 sales, the sequential
R/ingest_excel.R 83 reads the workbook: sheet reader, vintage switch, source directory
R/render.R 75 writes tables and figures to outputs/
R/run_sql.R 191 R twin of py/run_sql.py: run a shared .sql file against
R/site_exports.R 223 this site's data: the dials, the grid (computed by compute_tab5.R), input origins
R/tables.R 652 the six gt tables
R/verify.R 59 the test helper that compares R with the workbook's cached cells
R/vintage.R 206 everything that differs between the May and August workbooks, in one place
R/workbook_db.R 126 The public workbook's two large input sheets in SQLite, for the shared

Also: _targets.R (the pipeline: which function builds which result), tools/site-test-results.R (runs the suite for this page), and the site sources site/repro.qmd, site/materials.qmd, site/slides/slides.Rmd, site/index.html and site/explorer/index.html.

Data files the site publishes

file rows columns contents
grid.csv 1920 16 every cell of the explorer: six inputs and seven outcomes
inputs.csv 18 5 every input of the one-time estimate, with value, origin label and source
tab5.csv 4 8 Table 5, the four estimate rows, as the pipeline computes them
tab5-vs-printed.csv 28 7 every Table 5 cell against the digit printed in the paper
leavers.csv 7 4 the seven named leavers of Table 5 rows 3 and 4
test-results.csv 114 7 one row per test block from the last full run

Every file except test-results.csv is written by a target in _targets.R (site_grid_csv, site_inputs_csv, site_tab5_csv, site_tab5_printed_csv, site_leavers_csv; the explorer’s grid.js by site_grid_js). None is edited by hand.

Sources

The authors’ workbook and paper are not redistributed here. The manifest records where each file came from, its SHA-256 and its terms:

file provider obtained SHA-256 (first 12) terms
BSZ_MainTablesFigures.xlsx Boll Saez Zucman (via Saez lab, UC Berkeley) public-download 2026-10-14 ea0c14107e10 do-not-redistribute
september-2026/BSZ_MainTablesFigures.xlsx Boll Saez Zucman (via Saez lab, UC Berkeley) public-download 2026-09-22 c236cc941337 do-not-redistribute
BSZ26CAbillionaires.pdf NBER (Boll Saez Zucman) public-download 2026-09-22 ee1f2ea5a06c do-not-redistribute
may-2026/BSZ_MainTablesFigures.xlsx Boll Saez Zucman (via Saez lab, UC Berkeley) public-download 2026-05-26 cffe04bd950f do-not-redistribute
may-2026/BSZ26CAbillionaires.pdf NBER (Boll Saez Zucman) public-download 2026-05-26 cd8588edf529 do-not-redistribute
additional-documentation/galle-gamage-saez-shanskeCAbillionairetaxJuly26.pdf Galle, Gamage, Saez, Shanske public-download 2026-09-22 91d124417e5d do-not-redistribute
additional-documentation/galle-gamage-saez-shanskeCBTAcontrollingshares.pdf Galle, Gamage, Saez, Shanske public-download 2026-09-22 22d7f330ed06 do-not-redistribute
additional-documentation/galle-gamage-shanskeCBTAlegal.pdf Galle, Gamage, Shanske public-download 2026-09-22 ac75de294f93 do-not-redistribute
additional-documentation/GGS26props41-42.pdf Galle, Gamage, Shanske public-download 2026-09-22 97364601fbc3 do-not-redistribute
additional-documentation/responsetorauh26.pdf Galle, Gamage, Saez, Shanske (response to Rauh et al.) public-download 2026-09-22 c58b25e3540d do-not-redistribute
additional-documentation/saez-tpe26-presentation.pdf Saez public-download 2026-09-22 38c5cfd22421 do-not-redistribute
author-shared/2026-09-23_bsz-replication-code-data/ Boll, Saez, Zucman (author-shared) author-shared 2026-09-23 15dc5f6c3db5 confidential: use for re-analysis permitted; no public sharing

Full rows, including what each source feeds: DATA-SOURCES.csv; the column definitions: DATA-SOURCES.md. The author-shared bundle is confidential and is not used by anything published here.

What the checks found

  • A data-entry error in the workbook: rows 1338 to 1341 of data_sec_all repeated at rows 1342 to 1345 (244 billionaires counted instead of 240); handled in R/compute_data_sec_agg.R.
  • Two implicit readings in Table 5 (the literal 5% income tax loss share; row 4 omitting row 2’s phase-in deduction), kept at the workbook’s behaviour and disclosed on the explorer.
  • Two printed cells of Table 5 (the income tax loss in rows 2 and 3) that the paper rounds differently from its own workbook.
  • Display labels in this reproduction’s Tables 1 and A1 that do not match their values (GDP for AGI, 2025 for 2026).
  • A malformed row in our own source manifest: two provider fields contained an unquoted comma, which shifted every later column for any CSV reader; quoted on 2026-09-23.
  • Not reproduced: the paper’s Figure 8 and Appendix Figure A1 (wealth relative to AGI), and the raw data behind the workbook, which the pipeline takes as given.