// Builds the tables for the FactHub dataset top-rate-taxes-forecast-vs-actual (FactHub Research Team, 2026-10-21). // Every input number is typed from the cited source; ratios and counts are computed here. Run: node build_fva.js const fs = require('fs'); const path = require('path'); const out = __dirname; // estimate_usd_m is the point estimate, or the midpoint when the source gives a range (estimate_low/high). const cols = ['comparison_id', 'tax_id', 'tax', 'jurisdiction', 'period', 'estimator', 'estimate_made_on', 'estimate_kind', 'behavioral_assumption', 'estimate_usd_m', 'estimate_low_usd_m', 'estimate_high_usd_m', 'actual_usd_m', 'actual_kind', 'economy', 'main_explanation', 'verification', 'estimate_source_id', 'actual_source_id', 'label']; const NB = 'No behavioral adjustment stated'; const rows = [ ['ca-prop30-fy2012', 'ca-prop30', 'California Proposition 30 (2012) income-tax rates', 'California', 'FY2011-12', "Legislative Analyst's Office", '2012-09-24', 'Pre-enactment ballot estimate', NB, 2816, null, null, 3500, 'LAO estimate of revenue generated (Nov 2014)', 'Expansion', 'Partial year; LAO later found high earners accelerated income into 2012 ahead of federal rate increases', 'Verified', 'lao-prop30-2012', 'lao-prop30-2014', 'CA Prop 30 FY12'], ['ca-prop30-fy2013', 'ca-prop30', 'California Proposition 30 (2012) income-tax rates', 'California', 'FY2012-13', "Legislative Analyst's Office", '2012-09-24', 'Pre-enactment ballot estimate', NB, 4265, null, null, 5300, 'LAO estimate of revenue generated (Nov 2014); "over $5.3 billion", so a lower bound', 'Expansion', 'Stock-market gains; income accelerated into the 2012 tax year', 'Verified', 'lao-prop30-2012', 'lao-prop30-2014', 'CA Prop 30 FY13'], ['ca-prop30-fy2014', 'ca-prop30', 'California Proposition 30 (2012) income-tax rates', 'California', 'FY2013-14', "Legislative Analyst's Office", '2012-09-24', 'Pre-enactment ballot estimate', NB, 4358, null, null, 5600, 'LAO estimate of revenue generated (Nov 2014)', 'Expansion', 'Stock-market gains', 'Verified', 'lao-prop30-2012', 'lao-prop30-2014', 'CA Prop 30 FY14'], ['ca-prop30-fy2017', 'ca-prop30', 'California Proposition 30 (2012) income-tax rates', 'California', 'FY2016-17', "Legislative Analyst's Office", '2012-09-24', 'Pre-enactment ballot estimate', NB, 5173, null, null, 7000, 'State budget assumption reported by LAO in 2016 ("about $7 billion"); not a final tabulation', 'Expansion', 'Stock-market gains', 'Verified', 'lao-prop30-2012', 'lao-prop55-2016', 'CA Prop 30 FY17'], ['ca-prop63-fy2006', 'ca-prop63', 'California Proposition 63 (2004) 1% surcharge over $1M', 'California', 'FY2005-06', "Legislative Analyst's Office", '2004-07-01', 'Pre-enactment ballot estimate', NB, 750, null, null, 906, 'Revenue reported by the Mental Health Services Oversight and Accountability Commission (Jan 2017), cash basis', 'Expansion', 'Rising high-end incomes and capital gains', 'Verified', 'lao-prop63-2004', 'mhsoac-revenue-2017', 'CA Prop 63 FY06'], ['ca-prop63-fy2007', 'ca-prop63', 'California Proposition 63 (2004) 1% surcharge over $1M', 'California', 'FY2006-07', "Legislative Analyst's Office", '2004-07-01', 'Pre-enactment ballot estimate', NB, 800, null, null, 984, 'Revenue reported by the Mental Health Services Oversight and Accountability Commission (Jan 2017), cash basis', 'Expansion', 'Rising high-end incomes and capital gains', 'Verified', 'lao-prop63-2004', 'mhsoac-revenue-2017', 'CA Prop 63 FY07'], ['ma-surtax-fy2024-dor', 'ma-surtax', 'Massachusetts 4% surtax on income over $1M (2023)', 'Massachusetts', 'FY2024', 'Massachusetts Department of Revenue (consensus revenue hearing)', '2023-01-30', 'Pre-collection budget forecast (range)', 'Not stated', 1605.5, 1445, 1766, 2199, 'DOR preliminary certification (July 24, 2024)', 'Expansion', 'Strong 2023-24 markets and capital gains', 'Verified', 'mtf-fy2024-consensus', 'ma-dor-fy2024', 'MA surtax FY24 (DOR)'], ['ma-surtax-fy2024-tufts', 'ma-surtax', 'Massachusetts 4% surtax on income over $1M (2023)', 'Massachusetts', 'FY2024 (estimate for 2023)', 'Tufts Center for State Policy Analysis', '2022-01-15', 'Pre-enactment independent estimate', 'Dynamic: about 35% cut for avoidance and migration', 1300, null, null, 2199, 'DOR preliminary certification (July 24, 2024)', 'Expansion', 'The behavioral cut did not show up at the predicted size; strong markets', 'Verified', 'tufts-cspa-2022', 'ma-dor-fy2024', 'MA surtax FY24 (Tufts)'], ['ny-surcharge-sfy2010', 'ny-surcharge-2009', 'New York 2009 temporary high-income surcharge', 'New York', 'SFY2009-10', 'New York Division of the Budget (enacted budget)', '2009-04-07', 'Enacted-budget estimate', 'Not stated', 4000, null, null, 3600, 'DOB revised in-year forecast (Oct 30, 2009), not a final tabulation', 'Recession (2008-09)', 'Recession; DOB cited a historically unprecedented decline in wages among high earners', 'Verified', 'ny-dob-2009-midyear', 'ny-dob-2009-midyear', 'NY surcharge FY10'], ['md-millionaire-ty2008', 'md-millionaire', 'Maryland 6.25% bracket over $1M (2008-10)', 'Maryland', 'Tax year 2008', 'Maryland Department of Legislative Services', '2008-04-04', 'Pre-enactment fiscal note', 'Not stated for the income tax (the same note cut the sales-tax base by 6%)', 102.1, null, null, 78.2, 'Comptroller figure quoted in a DLS fiscal note (2012)', 'Recession (2008-09)', 'Recession; fewer and smaller millionaire incomes; migration claims disputed', 'Verified', 'md-dls-sb46-2008', 'md-dls-hb784-2012', 'MD $1M bracket TY08'], ['md-millionaire-ty2009', 'md-millionaire', 'Maryland 6.25% bracket over $1M (2008-10)', 'Maryland', 'Tax year 2009', 'Maryland Department of Legislative Services', '2008-04-04', 'Pre-enactment fiscal note', 'Not stated for the income tax (the same note cut the sales-tax base by 6%)', 109.3, null, null, 60.8, 'Comptroller figure quoted in a DLS fiscal note (2012)', 'Recession (2008-09)', 'Recession; fewer and smaller millionaire incomes; migration claims disputed', 'Verified', 'md-dls-sb46-2008', 'md-dls-hb784-2012', 'MD $1M bracket TY09'], ['or-m66-bien-2009-11', 'or-m66', 'Oregon Measure 66 (2010) top-rate increase', 'Oregon', '2009-11 biennium', 'Oregon Legislative Revenue Office', '2009-11-19', 'Pre-vote estimate', 'Static (behavior discussed only for long-run economic effects)', 504, null, null, 336, 'LRO revised projection (Dec 2010) of "about two-thirds" of $504 million, as reported by Oregon Business; $336M is our arithmetic', 'Recession (2008-09)', 'Recession and capital-gains collapse; tax year 2009 was taxed retroactively, so no one could avoid it', 'Unverified', 'or-lro-rr6-09', 'or-business-2010', 'OR Measure 66 2009-11'], ['mult-pfa-fy2022', 'mult-pfa', 'Multnomah County Preschool for All income tax (2021)', 'Multnomah County, Oregon', 'FY2022', 'Multnomah County', '2021-07-01', 'Budget forecast', 'Not stated', 112, null, null, 187, 'Collections reported by Multnomah County (Feb 14, 2023)', 'Expansion', '2021 capital gains; researchers estimate added out-migration cost about 8.5% of static revenue', 'Verified', 'mult-county-2023', 'mult-county-2023', 'Multnomah PFA FY22'], ['mult-pfa-fy2025', 'mult-pfa', 'Multnomah County Preschool for All income tax (2021)', 'Multnomah County, Oregon', 'FY2025', 'Multnomah County', '2024-07-01', 'Budget forecast', 'Not stated', 163, null, null, 224, 'Collections reported by Willamette Week from the county financial report', 'Expansion', 'Above forecast; includes a one-time lottery windfall', 'Unverified', 'wweek-pfa-2025', 'wweek-pfa-2025', 'Multnomah PFA FY25'], ['metro-shs-fy2025', 'metro-shs', 'Metro Supportive Housing Services income tax (2021)', 'Portland metro area, Oregon', 'FY2024-25', 'Metro', '2024-06-30', 'Budget forecast (original adopted budget)', 'Not stated', 374.5, null, null, 325.0, "Metro year-end report; within 0.6% of Metro's updated Fall 2024 forecast", 'Expansion', 'Post-2021 normalization; Metro cites a weaker local economy and a potential behavioral response', 'Verified', 'metro-shs-fy2025', 'metro-shs-fy2025', 'Metro SHS FY25'], ['wa-capgains-fy2023', 'wa-capgains', 'Washington capital gains tax (2021)', 'Washington', 'Tax year 2022 (FY2023)', 'Washington Department of Revenue (fiscal note)', '2021-06-22', 'Pre-enactment fiscal note (gross capital gains tax lines)', 'Static (2018 returns grown by the CBO forecast; compliance 85-95%)', 527, null, null, 782.0, 'DOR net payments as of June 9, 2026', 'Market decline (2022)', 'A few very large payments: the top 10 supplied 51% of tax year 2022', 'Verified', 'wa-fn-5096-2021', 'wa-dor-capgains-2026', 'WA cap gains TY22'], ['wa-capgains-2023-25', 'wa-capgains', 'Washington capital gains tax (2021)', 'Washington', 'Tax years 2023-24 (2023-25 biennium)', 'Washington Department of Revenue (fiscal note)', '2021-06-22', 'Pre-enactment fiscal note (gross capital gains tax lines)', 'Static (2018 returns grown by the CBO forecast; compliance 85-95%)', 1162, null, null, 978.5, 'DOR net payments as of June 9, 2026 ($394.7M + $583.8M)', 'Expansion', 'Fewer very large payments than in tax year 2022', 'Verified', 'wa-fn-5096-2021', 'wa-dor-capgains-2026', 'WA cap gains TY23-24'], ['wa-surcharge-fy2026', 'wa-surcharge', 'Washington 2.9% capital gains surcharge over $1M (2025)', 'Washington', 'Tax year 2025 (FY2026)', 'Washington Department of Revenue (fiscal note)', '2025-05-05', 'Pre-enactment fiscal note', 'Static (about 900 taxpayers; ERFC growth)', 151, null, null, 331.9, "ERFC estimate of the 2.9% rate's share of FY2026 collections, reported by the Washington Research Council", 'Expansion', 'Strong 2025 markets; the rate applied retroactively to Jan 1, 2025', 'Verified', 'wa-fn-5813-2025', 'wrc-capgains-2026', 'WA surcharge TY25'], ]; const recs = rows.map(r => { const o = {}; cols.forEach((c, i) => { o[c] = r[i]; }); o.ratio = Math.round(o.actual_usd_m / o.estimate_usd_m * 1000) / 1000; return o; }); const csvEsc = v => (v === null ? '' : (/[",\n]/.test(String(v)) ? '"' + String(v).replace(/"/g, '""') + '"' : String(v))); const writeCsv = (file, header, objs) => fs.writeFileSync(path.join(out, file), header.join(',') + '\n' + objs.map(o => header.map(c => csvEsc(o[c])).join(',')).join('\n') + '\n'); writeCsv('forecast_vs_actual.csv', [...cols, 'ratio'], recs); const ecols = ['estimate_id', 'tax', 'study', 'measure', 'percent', 'channel', 'caveat', 'source_id']; const erosion = [ ['rauh-shyu-y1', 'California Proposition 30 (2012)', 'Rauh & Shyu (2024)', 'Share of the expected windfall from the rate increase lost to behavioral responses in the first year (2013)', 45.2, 'Reported income and migration (migration was 9.5% of the total)', 'Measured against a no-response counterfactual, not against an official forecast', 'rauh-shyu-2024'], ['rauh-shyu-y2', 'California Proposition 30 (2012)', 'Rauh & Shyu (2024)', 'Same measure, within two years', 60.9, 'Reported income and migration', 'Includes timing responses around the retroactive 2012 rate', 'rauh-shyu-2024'], ['cir-pfa-2022', 'Multnomah County Preschool for All (2021)', 'Conway, Iselin & Rork (2026, working paper)', 'Share of static 2022 revenue lost to added out-migration', 8.5, 'Migration only', 'The authors call it likely an upper bound', 'cir-2026'], ['yv-nj-2006', 'New Jersey 2004 rate increase over $500k', 'Young & Varner (2011)', 'Annual migration revenue loss ($16.4M) as a share of the static yield ($1.08B, 2006); our calculation', 1.5, 'Migration only', 'The static yield is an upper bound; reported-income responses were not measured', 'young-varner-2011'], ['tufts-ma-proj', 'Massachusetts 4% surtax (2023)', 'Tufts Center for State Policy Analysis (2022)', 'Projected revenue cut from avoidance and migration (a projection, not a measurement)', 35, 'Reported income (about 30%) and migration (about 5%)', 'FY2024 collections were about 1.7 times the resulting estimate', 'tufts-cspa-2022'], ['wa-fn-6346', 'Washington ESSB 6346 (2026)', 'Washington Department of Revenue fiscal note', 'Behavioral response assumed in the official revenue estimate', 0, 'None assumed', 'The note says affected taxpayers do not reduce taxable income', 'wa-fn-6346-2026'], ]; const erecs = erosion.map(r => { const o = {}; ecols.forEach((c, i) => { o[c] = r[i]; }); return o; }); writeCsv('behavioral_erosion_estimates.csv', ecols, erecs); // Saved-analysis results const pick = o => ({ label: o.label, comparison_id: o.comparison_id, tax: o.tax, period: o.period, estimate_usd_m: o.estimate_usd_m, actual_usd_m: o.actual_usd_m, ratio: o.ratio, economy: o.economy, verification: o.verification }); const ratioRows = recs.map(pick); const verifiedRows = ratioRows.filter(o => o.verification === 'Verified'); const isRec = o => o.economy.startsWith('Recession'); const tally = (group, rs) => ({ group, comparisons: rs.length, above_estimate: rs.filter(o => o.ratio > 1).length, below_estimate: rs.filter(o => o.ratio < 1).length }); const summary = [ tally('All comparisons', ratioRows), tally('2008-09 recession years', ratioRows.filter(isRec)), tally('All other years', ratioRows.filter(o => !isRec(o))), tally('Verified comparisons only', verifiedRows), ]; const byTax = {}; recs.forEach(o => { const t = byTax[o.tax_id] || (byTax[o.tax_id] = { tax: o.tax, comparisons: 0, above_estimate: 0, below_estimate: 0, lowest_ratio: Infinity, highest_ratio: 0 }); t.comparisons++; if (o.ratio > 1) t.above_estimate++; else t.below_estimate++; t.lowest_ratio = Math.min(t.lowest_ratio, o.ratio); t.highest_ratio = Math.max(t.highest_ratio, o.ratio); }); const taxRows = Object.values(byTax).map(t => ({ ...t, result: t.below_estimate === 0 ? 'Above estimate in every comparison' : t.above_estimate === 0 ? 'Below estimate in every comparison' : 'Mixed' })); fs.writeFileSync(path.join(out, 'results.json'), JSON.stringify({ ratioRows, verifiedRows, summary, byTax: taxRows, erosion: erecs }, null, 1) + '\n'); console.log(JSON.stringify(summary, null, 1)); taxRows.forEach(t => console.log(t.tax + ': ' + t.result + ' (' + t.lowest_ratio + '-' + t.highest_ratio + ')')); ratioRows.forEach(o => console.log(o.label + ' ' + o.ratio));