| 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 |
Prop 40, BSZ analysis: the materials
Every script, test, data file and source behind the explorer, the report and the slides
BSZ analysis of California Proposition 40: Overview · Open Output: Explorer · Open Analysis: Report and Slides · Open Materials (this page is its guide)
Also in this project: Comparison overview · JR analysis overview
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
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_allrepeated at rows 1342 to 1345 (244 billionaires counted instead of 240); handled inR/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
providerfields 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.
BSZ analysis of California Proposition 40: Overview · Open Output: Explorer · Open Analysis: Report and Slides · Open Materials (this page is its guide)
Also in this project: Comparison overview · JR analysis overview
OPA done by Fernando Hoces de la Guardia, using agentic coding tools